diff --git a/.circleci/config.yml b/.circleci/config.yml index 524adc87d64..d4de5ede7c3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,21 +1,15 @@ version: 2.1 parameters: - check-legacy-node-version: - type: boolean - default: false - legacy-node-version: - type: string - default: '18.20' maintenance-node-version: type: string - default: '20.18' + default: '20.19' active-node-version: type: string - default: '22.11' + default: '22.14' current-node-version: type: string - default: '23.3' + default: '24.1' aliases: - &restore-cache @@ -62,61 +56,36 @@ jobs: docker: - image: cimg/node:<< parameters.node-version >> steps: + - checkout + - *restore-cache + - *install-deps + - *build-packages - when: condition: - and: - - equal: - [ - '<< parameters.node-version >>', - '<< pipeline.parameters.legacy-node-version >>', - ] - - not: << pipeline.parameters.check-legacy-node-version >> + equal: + [ + '<< parameters.node-version >>', + '<< pipeline.parameters.maintenance-node-version >>', + ] steps: - run: - name: Skip - command: | - echo Skipping + name: Test (coverage) + command: npm run test:cov + - run: + name: Collect coverage + command: npm run coverage + - store_artifacts: + path: coverage - when: condition: - or: - - not: - equal: - [ - '<< parameters.node-version >>', - '<< pipeline.parameters.legacy-node-version >>', - ] - - << pipeline.parameters.check-legacy-node-version >> + not: + equal: + [ + '<< parameters.node-version >>', + '<< pipeline.parameters.maintenance-node-version >>', + ] steps: - - checkout - - *restore-cache - - *install-deps - - *build-packages - - when: - condition: - equal: - [ - '<< parameters.node-version >>', - '<< pipeline.parameters.maintenance-node-version >>', - ] - steps: - - run: - name: Test (coverage) - command: npm run test:cov - - run: - name: Collect coverage - command: npm run coverage - - store_artifacts: - path: coverage - - when: - condition: - not: - equal: - [ - '<< parameters.node-version >>', - '<< pipeline.parameters.maintenance-node-version >>', - ] - steps: - - *run-unit-tests + - *run-unit-tests lint: working_directory: ~/nest @@ -183,7 +152,6 @@ jobs: name: Build all samples command: npm run build:samples - workflows: build-and-test: jobs: @@ -195,7 +163,6 @@ workflows: parameters: node-version: [ - '<< pipeline.parameters.legacy-node-version >>', '<< pipeline.parameters.maintenance-node-version >>', '<< pipeline.parameters.active-node-version >>', '<< pipeline.parameters.current-node-version >>', @@ -209,4 +176,3 @@ workflows: - samples: requires: - build - diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000000..30095a42e4e --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,18 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript"], + "categories": { + "correctness": "error" + }, + "env": { + "node": true + }, + "ignorePatterns": ["node_modules", "**/*.js", "**/*.d.ts"], + "rules": { + "no-unused-vars": "off", + "no-unused-expressions": "off", + "typescript/no-explicit-any": "off", + "typescript/no-non-null-asserted-optional-chain": "warn", + "typescript/no-require-imports": "off" + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 465ba827773..528740cec58 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,7 +116,6 @@ We cannot accept code without this. 1. In GitHub, send a pull request to `nestjs:master`. - If we suggest changes then: - - Make the required updates. - Re-run the Nest test suites to ensure tests are still passing. - Rebase your branch and force push to your GitHub repository (this will update your Pull Request): @@ -159,7 +158,7 @@ from the main (upstream) repository: ## Development Setup -You will need [Node.js](https://nodejs.org) version >= 10.13.0 (except for v13). +You will need [Node.js](https://nodejs.org) version >= 20. 1. After cloning the repo, run: @@ -322,8 +321,10 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise [commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit# + + [dev-doc]: https://github.com/nestjs/nest/blob/master/docs/DEVELOPER.md [github]: https://github.com/nestjs/nest [stackoverflow]: https://stackoverflow.com/questions/tagged/nestjs diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 194f3e0de22..00000000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,55 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['node_modules', '**/node_modules/**', '**/*.js', '**/*.d.ts'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-function-type': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unused-expressions': 'off', - '@typescript-eslint/no-require-imports': 'off', - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/no-non-null-asserted-optional-chain': 'warn', - '@typescript-eslint/no-misused-promises': [ - 'error', - { - checksVoidReturn: false, - checksConditionals: false, - }, - ], - '@typescript-eslint/require-await': 'off', - '@typescript-eslint/prefer-promise-reject-errors': 'off', - '@typescript-eslint/no-base-to-string': 'off', - '@typescript-eslint/unbound-method': 'off', - '@typescript-eslint/only-throw-error': 'off', - }, - }, -); diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index a0962e1a387..00000000000 --- a/gulpfile.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; -/** - * Load the TypeScript compiler, then load the TypeScript gulpfile which simply loads all - * the tasks. The tasks are really inside tools/gulp/tasks. - */ - -const path = require('path'); - -const projectDir = __dirname; -const tsconfigPath = path.join(projectDir, 'tools/gulp/tsconfig.json'); - -require('ts-node').register({ - project: tsconfigPath -}); - -require('./tools/gulp/gulpfile'); \ No newline at end of file diff --git a/gulpfile.mjs b/gulpfile.mjs new file mode 100644 index 00000000000..45cc068b5f8 --- /dev/null +++ b/gulpfile.mjs @@ -0,0 +1,13 @@ +/** + * Load the TypeScript compiler, then load the TypeScript gulpfile which simply loads all + * the tasks. The tasks are really inside tools/gulp/tasks. + */ + +import { register } from 'node:module'; +import { pathToFileURL } from 'node:url'; + +register('ts-node/esm', pathToFileURL('./'), { + data: { project: './tools/gulp/tsconfig.json' }, +}); + +await import('./tools/gulp/gulpfile.ts'); diff --git a/hooks/mocha-init-hook.ts b/hooks/mocha-init-hook.ts deleted file mode 100644 index 82b14a111af..00000000000 --- a/hooks/mocha-init-hook.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const mochaHooks = (): Mocha.RootHookObject => { - return { - async beforeAll(this: Mocha.Context) { - await import('reflect-metadata'); - }, - }; -}; diff --git a/integration/auto-mock/src/bar.service.ts b/integration/auto-mock/src/bar.service.ts index 5b75933f90d..24b0124ec44 100644 --- a/integration/auto-mock/src/bar.service.ts +++ b/integration/auto-mock/src/bar.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { FooService } from './foo.service'; +import { FooService } from './foo.service.js'; @Injectable() export class BarService { diff --git a/integration/auto-mock/test/bar.service.spec.ts b/integration/auto-mock/test/bar.service.spec.ts index c004bbf67fe..a1359799dc6 100644 --- a/integration/auto-mock/test/bar.service.spec.ts +++ b/integration/auto-mock/test/bar.service.spec.ts @@ -1,17 +1,11 @@ import { Test } from '@nestjs/testing'; -import * as chai from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; -import * as sinon from 'sinon'; -import { BarService } from '../src/bar.service'; -import { FooService } from '../src/foo.service'; - -chai.use(chaiAsPromised); -const { expect } = chai; +import { BarService } from '../src/bar.service.js'; +import { FooService } from '../src/foo.service.js'; describe('Auto-Mocking Bar Deps', () => { let service: BarService; let fooService: FooService; - const stub = sinon.stub(); + const stub = vi.fn(); beforeEach(async () => { const moduleRef = await Test.createTestingModule({ providers: [BarService], @@ -23,12 +17,12 @@ describe('Auto-Mocking Bar Deps', () => { }); it('should be defined', () => { - expect(service).not.to.be.undefined; - expect(fooService).not.to.be.undefined; + expect(service).not.toBeUndefined(); + expect(fooService).not.toBeUndefined(); }); it('should call bar.bar', () => { service.bar(); - expect(stub.called); + expect(stub).toHaveBeenCalled(); }); }); @@ -39,23 +33,25 @@ describe('Auto-Mocking with token in factory', () => { }) .useMocker(token => { if (token === FooService) { - return { foo: sinon.stub }; + return { foo: vi.fn() }; } }) .compile(); const service = moduleRef.get(BarService); - const fooServ = moduleRef.get<{ foo: sinon.SinonStub }>(FooService as any); + const fooServ = moduleRef.get<{ foo: ReturnType }>( + FooService as any, + ); service.bar(); - expect(fooServ.foo.called); + expect(fooServ.foo).toHaveBeenCalled(); }); it('cannot mock the dependencies', async () => { const moduleRef = Test.createTestingModule({ providers: [BarService], }).useMocker(token => { if (token === FooService.name + 'something that fails the token') { - return { foo: sinon.stub }; + return { foo: vi.fn() }; } }).compile; - expect(moduleRef()).to.eventually.throw(); + await expect(moduleRef()).rejects.toThrow(); }); }); diff --git a/integration/auto-mock/tsconfig.json b/integration/auto-mock/tsconfig.json index 2d3b89de293..34056a28cae 100644 --- a/integration/auto-mock/tsconfig.json +++ b/integration/auto-mock/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/cors/e2e/express.spec.ts b/integration/cors/e2e/express.spec.ts index 8e268c3166e..da3f738e049 100644 --- a/integration/cors/e2e/express.spec.ts +++ b/integration/cors/e2e/express.spec.ts @@ -1,7 +1,7 @@ import { NestExpressApplication } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Express Cors', () => { let app: NestExpressApplication; @@ -25,7 +25,7 @@ describe('Express Cors', () => { ]; describe('Dynamic config', () => { describe('enableCors', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -66,13 +66,13 @@ describe('Express Cors', () => { .expect('content-length', '0'); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); describe('Application Options', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -114,14 +114,14 @@ describe('Express Cors', () => { .expect('content-length', '0'); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); }); describe('Static config', () => { describe('enableCors', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -141,14 +141,14 @@ describe('Express Cors', () => { .expect('access-control-expose-headers', 'foo,bar') .expect('content-length', '0'); }); - }); - after(async () => { - await app.close(); + afterAll(async () => { + await app.close(); + }); }); describe('Application Options', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -169,7 +169,7 @@ describe('Express Cors', () => { .expect('content-length', '0'); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/cors/e2e/fastify.spec.ts b/integration/cors/e2e/fastify.spec.ts index c24bfc6ff39..28d468fcc98 100644 --- a/integration/cors/e2e/fastify.spec.ts +++ b/integration/cors/e2e/fastify.spec.ts @@ -3,8 +3,8 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe.skip('Fastify Cors', () => { let app: NestFastifyApplication; @@ -28,7 +28,7 @@ describe.skip('Fastify Cors', () => { ]; describe('Dynamic config', () => { describe('enableCors', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -73,13 +73,13 @@ describe.skip('Fastify Cors', () => { .expect('content-length', '0'); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); describe('Application Options', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -124,7 +124,7 @@ describe.skip('Fastify Cors', () => { .expect('content-length', '0'); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); @@ -132,7 +132,7 @@ describe.skip('Fastify Cors', () => { describe('Static config', () => { describe('enableCors', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -154,14 +154,14 @@ describe.skip('Fastify Cors', () => { .expect('access-control-expose-headers', 'foo,bar') .expect('content-length', '0'); }); - }); - after(async () => { - await app.close(); + afterAll(async () => { + await app.close(); + }); }); describe('Application Options', () => { - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -184,10 +184,10 @@ describe.skip('Fastify Cors', () => { .expect('access-control-expose-headers', 'foo,bar') .expect('content-length', '0'); }); - }); - after(async () => { - await app.close(); + afterAll(async () => { + await app.close(); + }); }); }); }); diff --git a/integration/cors/src/app.module.ts b/integration/cors/src/app.module.ts index 848d4aaa7fe..52bb569b5d1 100644 --- a/integration/cors/src/app.module.ts +++ b/integration/cors/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; @Module({ controllers: [AppController], diff --git a/integration/cors/tsconfig.json b/integration/cors/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/cors/tsconfig.json +++ b/integration/cors/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/discovery/e2e/discover-by-meta.spec.ts b/integration/discovery/e2e/discover-by-meta.spec.ts index 679c7758430..99af0b40822 100644 --- a/integration/discovery/e2e/discover-by-meta.spec.ts +++ b/integration/discovery/e2e/discover-by-meta.spec.ts @@ -1,9 +1,8 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { DiscoveryService } from '@nestjs/core'; -import { expect } from 'chai'; -import { AppModule } from '../src/app.module'; -import { WebhooksExplorer } from '../src/webhooks.explorer'; -import { NonAppliedDecorator } from '../src/decorators/non-applied.decorator'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../src/app.module.js'; +import { NonAppliedDecorator } from '../src/decorators/non-applied.decorator.js'; +import { WebhooksExplorer } from '../src/webhooks.explorer.js'; describe('DiscoveryModule', () => { let moduleRef: TestingModule; @@ -14,10 +13,14 @@ describe('DiscoveryModule', () => { }).compile(); }); + afterEach(async () => { + await moduleRef.close(); + }); + it('should discover all providers & handlers with corresponding annotations', async () => { const webhooksExplorer = moduleRef.get(WebhooksExplorer); - expect(webhooksExplorer.getWebhooks()).to.be.eql([ + expect(webhooksExplorer.getWebhooks()).toEqual([ { handlers: [ { @@ -45,7 +48,7 @@ describe('DiscoveryModule', () => { const providers = discoveryService.getProviders({ metadataKey: NonAppliedDecorator.KEY, }); - expect(providers).to.be.eql([]); + expect(providers).toEqual([]); }); it('should return an empty array if no controllers were found for a given discoverable decorator', () => { @@ -54,6 +57,6 @@ describe('DiscoveryModule', () => { const controllers = discoveryService.getControllers({ metadataKey: NonAppliedDecorator.KEY, }); - expect(controllers).to.be.eql([]); + expect(controllers).toEqual([]); }); }); diff --git a/integration/discovery/src/app.module.ts b/integration/discovery/src/app.module.ts index c0d93e30c34..ee503ab5d9d 100644 --- a/integration/discovery/src/app.module.ts +++ b/integration/discovery/src/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { DiscoveryModule } from '@nestjs/core'; -import { MyWebhookModule } from './my-webhook/my-webhook.module'; -import { WebhooksExplorer } from './webhooks.explorer'; +import { MyWebhookModule } from './my-webhook/my-webhook.module.js'; +import { WebhooksExplorer } from './webhooks.explorer.js'; @Module({ imports: [MyWebhookModule, DiscoveryModule], diff --git a/integration/discovery/src/my-webhook/cleanup.webhook.ts b/integration/discovery/src/my-webhook/cleanup.webhook.ts index 11364d87c22..a16bc452a10 100644 --- a/integration/discovery/src/my-webhook/cleanup.webhook.ts +++ b/integration/discovery/src/my-webhook/cleanup.webhook.ts @@ -1,4 +1,4 @@ -import { Webhook, WebhookHandler } from '../decorators/webhook.decorators'; +import { Webhook, WebhookHandler } from '../decorators/webhook.decorators.js'; @Webhook({ name: 'cleanup' }) export class CleanupWebhook { diff --git a/integration/discovery/src/my-webhook/flush.webhook.ts b/integration/discovery/src/my-webhook/flush.webhook.ts index e1db9308786..ba43ae2b447 100644 --- a/integration/discovery/src/my-webhook/flush.webhook.ts +++ b/integration/discovery/src/my-webhook/flush.webhook.ts @@ -1,4 +1,4 @@ -import { Webhook, WebhookHandler } from '../decorators/webhook.decorators'; +import { Webhook, WebhookHandler } from '../decorators/webhook.decorators.js'; @Webhook({ name: 'flush' }) export class FlushWebhook { diff --git a/integration/discovery/src/my-webhook/my-webhook.module.ts b/integration/discovery/src/my-webhook/my-webhook.module.ts index d868b95d993..af9acd6dd3e 100644 --- a/integration/discovery/src/my-webhook/my-webhook.module.ts +++ b/integration/discovery/src/my-webhook/my-webhook.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CleanupWebhook } from './cleanup.webhook'; -import { FlushWebhook } from './flush.webhook'; +import { CleanupWebhook } from './cleanup.webhook.js'; +import { FlushWebhook } from './flush.webhook.js'; @Module({ providers: [CleanupWebhook, FlushWebhook] }) export class MyWebhookModule {} diff --git a/integration/discovery/src/webhooks.explorer.ts b/integration/discovery/src/webhooks.explorer.ts index d449950a542..a827c7ff20e 100644 --- a/integration/discovery/src/webhooks.explorer.ts +++ b/integration/discovery/src/webhooks.explorer.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { DiscoveryService, MetadataScanner } from '@nestjs/core'; -import { Webhook, WebhookHandler } from './decorators/webhook.decorators'; +import { Webhook, WebhookHandler } from './decorators/webhook.decorators.js'; @Injectable() export class WebhooksExplorer { diff --git a/integration/discovery/tsconfig.json b/integration/discovery/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/discovery/tsconfig.json +++ b/integration/discovery/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/graceful-shutdown/e2e/express.spec.ts b/integration/graceful-shutdown/e2e/express.spec.ts new file mode 100644 index 00000000000..62c84407e40 --- /dev/null +++ b/integration/graceful-shutdown/e2e/express.spec.ts @@ -0,0 +1,93 @@ +import { NestFactory } from '@nestjs/core'; +import { ExpressAdapter } from '@nestjs/platform-express'; +import { INestApplication } from '@nestjs/common'; +import * as http from 'http'; +import { AppModule } from '../src/app.module.js'; + +describe('Graceful Shutdown (Express)', () => { + let app: INestApplication; + + afterEach(async () => { + if (app) { + await app.close(); + } + }); + + it('should allow in-flight requests to complete when return503OnClosing is enabled', async () => { + app = await NestFactory.create(AppModule, new ExpressAdapter() as any, { + return503OnClosing: true, + logger: false, + }); + await app.listen(0); + const port = app.getHttpServer().address().port; + + const requestPromise = new Promise((resolve, reject) => { + http + .get( + `http://localhost:${port}/slow`, + { + // Explicitly close connection after response to speed up server shutdown + headers: { Connection: 'close' }, + }, + res => { + let data = ''; + res.on('data', c => (data += c)); + res.on('end', () => resolve(data)); + }, + ) + .on('error', reject); + }); + + // Wait to ensure request is processing + await new Promise(r => setTimeout(r, 100)); + + const closePromise = app.close(); + + // The in-flight request should finish successfully + const response = await requestPromise; + expect(response).toBe('ok'); + + await closePromise; + }, 10000); + + it('should return 503 for NEW queued requests on existing connections during shutdown', async () => { + app = await NestFactory.create(AppModule, new ExpressAdapter() as any, { + return503OnClosing: true, + logger: false, + }); + await app.listen(0); + const port = app.getHttpServer().address().port; + + // Force 1 socket to ensure queuing/reuse + const agent = new http.Agent({ keepAlive: true, maxSockets: 1 }); + + // 1. Send Request A (slow) - occupies the socket + const req1 = http.get(`http://localhost:${port}/slow`, { agent }); + + // 2. Wait so Request A is definitely "in flight" + await new Promise(r => setTimeout(r, 100)); + + // 3. Trigger Shutdown (don't await yet) + const closePromise = app.close(); + + // Allow the microtask for prepareClose() to flush (sets isShuttingDown) + await new Promise(r => setTimeout(r, 0)); + + // 4. Send Request B immediately using the same agent. + const statusPromise = new Promise((resolve, reject) => { + const req = http.get(`http://localhost:${port}/slow`, { agent }, res => { + resolve(res.statusCode || 0); + }); + req.on('error', reject); + }); + + // 5. Cleanup Request A + req1.on('error', () => {}); + + const status = await statusPromise; + expect(status).toBe(503); + + await closePromise; + agent.destroy(); + }, 10000); +}); diff --git a/integration/graceful-shutdown/src/app.controller.ts b/integration/graceful-shutdown/src/app.controller.ts new file mode 100644 index 00000000000..27fa610ea73 --- /dev/null +++ b/integration/graceful-shutdown/src/app.controller.ts @@ -0,0 +1,11 @@ +import { Controller, Get } from '@nestjs/common'; + +@Controller() +export class AppController { + @Get('slow') + async slow() { + // Simulate work + await new Promise(resolve => setTimeout(resolve, 500)); + return 'ok'; + } +} diff --git a/integration/graceful-shutdown/src/app.module.ts b/integration/graceful-shutdown/src/app.module.ts new file mode 100644 index 00000000000..52bb569b5d1 --- /dev/null +++ b/integration/graceful-shutdown/src/app.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller.js'; + +@Module({ + controllers: [AppController], +}) +export class AppModule {} diff --git a/integration/graceful-shutdown/tsconfig.json b/integration/graceful-shutdown/tsconfig.json new file mode 100644 index 00000000000..7d3e3a2d9a9 --- /dev/null +++ b/integration/graceful-shutdown/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "types": ["vitest/globals"], + "module": "commonjs", + "declaration": false, + "noImplicitAny": false, + "removeComments": true, + "noLib": false, + "esModuleInterop": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2023", + "sourceMap": true, + "allowJs": true, + "strictNullChecks": true, + "outDir": "./dist", + "paths": { + "@nestjs/common": ["../../packages/common"], + "@nestjs/common/*": ["../../packages/common/*"], + "@nestjs/core": ["../../packages/core"], + "@nestjs/core/*": ["../../packages/core/*"], + "@nestjs/platform-express": ["../../packages/platform-express"], + "@nestjs/platform-express/*": ["../../packages/platform-express/*"], + "@nestjs/testing": ["../../packages/testing"], + "@nestjs/testing/*": ["../../packages/testing/*"] + } + }, + "include": ["src/**/*", "e2e/**/*"], + "exclude": ["node_modules"] +} diff --git a/integration/graphql-code-first/e2e/code-first.spec.ts b/integration/graphql-code-first/e2e/code-first.spec.ts index 258edd8a90f..18ad0112dd8 100644 --- a/integration/graphql-code-first/e2e/code-first.spec.ts +++ b/integration/graphql-code-first/e2e/code-first.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('GraphQL - Code-first', () => { let app: INestApplication; diff --git a/integration/graphql-code-first/e2e/guards-filters.spec.ts b/integration/graphql-code-first/e2e/guards-filters.spec.ts index 4953d771ca8..c4481bab121 100644 --- a/integration/graphql-code-first/e2e/guards-filters.spec.ts +++ b/integration/graphql-code-first/e2e/guards-filters.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('GraphQL - Guards', () => { let app: INestApplication; diff --git a/integration/graphql-code-first/e2e/pipes.spec.ts b/integration/graphql-code-first/e2e/pipes.spec.ts index e14e31df183..ad51fd71ae7 100644 --- a/integration/graphql-code-first/e2e/pipes.spec.ts +++ b/integration/graphql-code-first/e2e/pipes.spec.ts @@ -1,7 +1,7 @@ import { INestApplication, ValidationPipe } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('GraphQL Pipes', () => { let app: INestApplication; diff --git a/integration/graphql-code-first/src/app.module.ts b/integration/graphql-code-first/src/app.module.ts index 5da27fa35a2..eddfa249bba 100644 --- a/integration/graphql-code-first/src/app.module.ts +++ b/integration/graphql-code-first/src/app.module.ts @@ -2,7 +2,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { join } from 'path'; -import { RecipesModule } from './recipes/recipes.module'; +import { RecipesModule } from './recipes/recipes.module.js'; @Module({ imports: [ diff --git a/integration/graphql-code-first/src/main.ts b/integration/graphql-code-first/src/main.ts index 4561536fa34..a812539be92 100644 --- a/integration/graphql-code-first/src/main.ts +++ b/integration/graphql-code-first/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/integration/graphql-code-first/src/recipes/recipes.module.ts b/integration/graphql-code-first/src/recipes/recipes.module.ts index efb8dfa837b..77b8eb5af20 100644 --- a/integration/graphql-code-first/src/recipes/recipes.module.ts +++ b/integration/graphql-code-first/src/recipes/recipes.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { APP_FILTER } from '@nestjs/core'; -import { UnauthorizedFilter } from '../common/filters/unauthorized.filter'; -import { DateScalar } from '../common/scalars/date.scalar'; -import { RecipesResolver } from './recipes.resolver'; -import { RecipesService } from './recipes.service'; +import { UnauthorizedFilter } from '../common/filters/unauthorized.filter.js'; +import { DateScalar } from '../common/scalars/date.scalar.js'; +import { RecipesResolver } from './recipes.resolver.js'; +import { RecipesService } from './recipes.service.js'; @Module({ providers: [ diff --git a/integration/graphql-code-first/src/recipes/recipes.resolver.ts b/integration/graphql-code-first/src/recipes/recipes.resolver.ts index a91459c5d72..cf39f923694 100644 --- a/integration/graphql-code-first/src/recipes/recipes.resolver.ts +++ b/integration/graphql-code-first/src/recipes/recipes.resolver.ts @@ -1,12 +1,12 @@ import { NotFoundException, UseGuards, UseInterceptors } from '@nestjs/common'; import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql'; import { PubSub } from 'graphql-subscriptions'; -import { AuthGuard } from '../common/guards/auth.guard'; -import { DataInterceptor } from '../common/interceptors/data.interceptor'; -import { NewRecipeInput } from './dto/new-recipe.input'; -import { RecipesArgs } from './dto/recipes.args'; -import { Recipe } from './models/recipe'; -import { RecipesService } from './recipes.service'; +import { AuthGuard } from '../common/guards/auth.guard.js'; +import { DataInterceptor } from '../common/interceptors/data.interceptor.js'; +import { NewRecipeInput } from './dto/new-recipe.input.js'; +import { RecipesArgs } from './dto/recipes.args.js'; +import { Recipe } from './models/recipe.js'; +import { RecipesService } from './recipes.service.js'; const pubSub = new PubSub(); diff --git a/integration/graphql-code-first/src/recipes/recipes.service.ts b/integration/graphql-code-first/src/recipes/recipes.service.ts index ba2ab579193..ae4eabed38c 100644 --- a/integration/graphql-code-first/src/recipes/recipes.service.ts +++ b/integration/graphql-code-first/src/recipes/recipes.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { NewRecipeInput } from './dto/new-recipe.input'; -import { RecipesArgs } from './dto/recipes.args'; -import { Recipe } from './models/recipe'; +import { NewRecipeInput } from './dto/new-recipe.input.js'; +import { RecipesArgs } from './dto/recipes.args.js'; +import { Recipe } from './models/recipe.js'; @Injectable() export class RecipesService { diff --git a/integration/graphql-code-first/tsconfig.json b/integration/graphql-code-first/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/graphql-code-first/tsconfig.json +++ b/integration/graphql-code-first/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/graphql-schema-first/e2e/graphql-async-class.spec.ts b/integration/graphql-schema-first/e2e/graphql-async-class.spec.ts index 6ed8d946829..741ea679ef5 100644 --- a/integration/graphql-schema-first/e2e/graphql-async-class.spec.ts +++ b/integration/graphql-schema-first/e2e/graphql-async-class.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import * as request from 'supertest'; -import { AsyncClassApplicationModule } from '../src/async-options-class.module'; +import request from 'supertest'; +import { AsyncClassApplicationModule } from '../src/async-options-class.module.js'; describe('GraphQL (async class)', () => { let app: INestApplication; diff --git a/integration/graphql-schema-first/e2e/graphql-async-existing.spec.ts b/integration/graphql-schema-first/e2e/graphql-async-existing.spec.ts index 23102984871..8b379f1bff7 100644 --- a/integration/graphql-schema-first/e2e/graphql-async-existing.spec.ts +++ b/integration/graphql-schema-first/e2e/graphql-async-existing.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import * as request from 'supertest'; -import { AsyncExistingApplicationModule } from '../src/async-options-existing.module'; +import request from 'supertest'; +import { AsyncExistingApplicationModule } from '../src/async-options-existing.module.js'; describe('GraphQL (async existing)', () => { let app: INestApplication; diff --git a/integration/graphql-schema-first/e2e/graphql-async.spec.ts b/integration/graphql-schema-first/e2e/graphql-async.spec.ts index cf2cd47b4c5..cc15515084a 100644 --- a/integration/graphql-schema-first/e2e/graphql-async.spec.ts +++ b/integration/graphql-schema-first/e2e/graphql-async.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import * as request from 'supertest'; -import { AsyncApplicationModule } from '../src/async-options.module'; +import request from 'supertest'; +import { AsyncApplicationModule } from '../src/async-options.module.js'; describe('GraphQL (async configuration)', () => { let app: INestApplication; diff --git a/integration/graphql-schema-first/e2e/graphql-request-scoped.spec.ts b/integration/graphql-schema-first/e2e/graphql-request-scoped.spec.ts index 125a55c8151..014ae01b755 100644 --- a/integration/graphql-schema-first/e2e/graphql-request-scoped.spec.ts +++ b/integration/graphql-schema-first/e2e/graphql-request-scoped.spec.ts @@ -2,11 +2,10 @@ import { ApolloDriver } from '@nestjs/apollo'; import { INestApplication } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { join } from 'path'; -import * as request from 'supertest'; -import { CatsRequestScopedService } from '../src/cats/cats-request-scoped.service'; -import { CatsModule } from '../src/cats/cats.module'; +import request from 'supertest'; +import { CatsRequestScopedService } from '../src/cats/cats-request-scoped.service.js'; +import { CatsModule } from '../src/cats/cats.module.js'; describe('GraphQL request scoped', () => { let app: INestApplication; @@ -17,7 +16,9 @@ describe('GraphQL request scoped', () => { CatsModule.enableRequestScope(), GraphQLModule.forRoot({ driver: ApolloDriver, - typePaths: [join(__dirname, '..', 'src', '**', '*.graphql')], + typePaths: [ + join(import.meta.dirname, '..', 'src', '**', '*.graphql'), + ], }), ], }).compile(); @@ -53,7 +54,7 @@ describe('GraphQL request scoped', () => { }); it(`should create resolver for each incoming request`, () => { - expect(CatsRequestScopedService.COUNTER).to.be.eql(3); + expect(CatsRequestScopedService.COUNTER).toEqual(3); }); afterEach(async () => { diff --git a/integration/graphql-schema-first/e2e/graphql.spec.ts b/integration/graphql-schema-first/e2e/graphql.spec.ts index e8ced186f3f..a3f07f1186b 100644 --- a/integration/graphql-schema-first/e2e/graphql.spec.ts +++ b/integration/graphql-schema-first/e2e/graphql.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('GraphQL', () => { let app: INestApplication; diff --git a/integration/graphql-schema-first/src/app.module.ts b/integration/graphql-schema-first/src/app.module.ts index 2879cfc9f30..0f9be0cc428 100644 --- a/integration/graphql-schema-first/src/app.module.ts +++ b/integration/graphql-schema-first/src/app.module.ts @@ -2,7 +2,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { join } from 'path'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [ @@ -10,7 +10,7 @@ import { CatsModule } from './cats/cats.module'; GraphQLModule.forRoot({ driver: ApolloDriver, includeStacktraceInErrorResponses: true, - typePaths: [join(__dirname, '**', '*.graphql')], + typePaths: [join(import.meta.dirname, '**', '*.graphql')], }), ], }) diff --git a/integration/graphql-schema-first/src/async-options-class.module.ts b/integration/graphql-schema-first/src/async-options-class.module.ts index 5e702348ddf..164abdfceb9 100644 --- a/integration/graphql-schema-first/src/async-options-class.module.ts +++ b/integration/graphql-schema-first/src/async-options-class.module.ts @@ -2,12 +2,12 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GqlOptionsFactory, GraphQLModule } from '@nestjs/graphql'; import { join } from 'path'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; class ConfigService implements GqlOptionsFactory { createGqlOptions(): ApolloDriverConfig { return { - typePaths: [join(__dirname, '**', '*.graphql')], + typePaths: [join(import.meta.dirname, '**', '*.graphql')], }; } } diff --git a/integration/graphql-schema-first/src/async-options-existing.module.ts b/integration/graphql-schema-first/src/async-options-existing.module.ts index e68b65ada6f..9c6b5363bfc 100644 --- a/integration/graphql-schema-first/src/async-options-existing.module.ts +++ b/integration/graphql-schema-first/src/async-options-existing.module.ts @@ -1,9 +1,9 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; -import { CatsModule } from './cats/cats.module'; -import { ConfigModule } from './config.module'; -import { ConfigService } from './config.service'; +import { CatsModule } from './cats/cats.module.js'; +import { ConfigModule } from './config.module.js'; +import { ConfigService } from './config.service.js'; @Module({ imports: [ diff --git a/integration/graphql-schema-first/src/async-options.module.ts b/integration/graphql-schema-first/src/async-options.module.ts index c123cbc97ab..1068c78c687 100644 --- a/integration/graphql-schema-first/src/async-options.module.ts +++ b/integration/graphql-schema-first/src/async-options.module.ts @@ -2,7 +2,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { join } from 'path'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [ @@ -10,7 +10,7 @@ import { CatsModule } from './cats/cats.module'; GraphQLModule.forRootAsync({ driver: ApolloDriver, useFactory: async () => ({ - typePaths: [join(__dirname, '**', '*.graphql')], + typePaths: [join(import.meta.dirname, '**', '*.graphql')], }), }), ], diff --git a/integration/graphql-schema-first/src/cats/cats-request-scoped.service.ts b/integration/graphql-schema-first/src/cats/cats-request-scoped.service.ts index ebb88880d4d..1338a78a862 100644 --- a/integration/graphql-schema-first/src/cats/cats-request-scoped.service.ts +++ b/integration/graphql-schema-first/src/cats/cats-request-scoped.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { Cat } from './interfaces/cat.interface'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable({ scope: Scope.REQUEST }) export class CatsRequestScopedService { diff --git a/integration/graphql-schema-first/src/cats/cats.module.ts b/integration/graphql-schema-first/src/cats/cats.module.ts index b2fa3f9a974..572fa38c22b 100644 --- a/integration/graphql-schema-first/src/cats/cats.module.ts +++ b/integration/graphql-schema-first/src/cats/cats.module.ts @@ -1,7 +1,7 @@ import { DynamicModule, Module, Scope } from '@nestjs/common'; -import { CatsRequestScopedService } from './cats-request-scoped.service'; -import { CatsResolvers } from './cats.resolvers'; -import { CatsService } from './cats.service'; +import { CatsRequestScopedService } from './cats-request-scoped.service.js'; +import { CatsResolvers } from './cats.resolvers.js'; +import { CatsService } from './cats.service.js'; @Module({ providers: [CatsService, CatsResolvers], diff --git a/integration/graphql-schema-first/src/cats/cats.resolvers.ts b/integration/graphql-schema-first/src/cats/cats.resolvers.ts index fcc08097aa6..d537f4f4821 100644 --- a/integration/graphql-schema-first/src/cats/cats.resolvers.ts +++ b/integration/graphql-schema-first/src/cats/cats.resolvers.ts @@ -1,9 +1,9 @@ import { ParseIntPipe, UseGuards } from '@nestjs/common'; import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql'; import { PubSub } from 'graphql-subscriptions'; -import { CatsGuard } from './cats.guard'; -import { CatsService } from './cats.service'; -import { Cat } from './interfaces/cat.interface'; +import { CatsGuard } from './cats.guard.js'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; const pubSub = new PubSub(); diff --git a/integration/graphql-schema-first/src/cats/cats.service.ts b/integration/graphql-schema-first/src/cats/cats.service.ts index a529301e6b5..397fd1fbc77 100644 --- a/integration/graphql-schema-first/src/cats/cats.service.ts +++ b/integration/graphql-schema-first/src/cats/cats.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Cat } from './interfaces/cat.interface'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable() export class CatsService { diff --git a/integration/graphql-schema-first/src/config.module.ts b/integration/graphql-schema-first/src/config.module.ts index eaa505c8462..bf41826bbf7 100644 --- a/integration/graphql-schema-first/src/config.module.ts +++ b/integration/graphql-schema-first/src/config.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { ConfigService } from './config.service'; +import { ConfigService } from './config.service.js'; @Module({ providers: [ConfigService], diff --git a/integration/graphql-schema-first/src/config.service.ts b/integration/graphql-schema-first/src/config.service.ts index 66ed7ee022f..bbdabd115f0 100644 --- a/integration/graphql-schema-first/src/config.service.ts +++ b/integration/graphql-schema-first/src/config.service.ts @@ -6,7 +6,7 @@ import { join } from 'path'; export class ConfigService implements GqlOptionsFactory { createGqlOptions(): GqlModuleOptions { return { - typePaths: [join(__dirname, '**', '*.graphql')], + typePaths: [join(import.meta.dirname, '**', '*.graphql')], }; } } diff --git a/integration/graphql-schema-first/src/main.ts b/integration/graphql-schema-first/src/main.ts index 23f3240c271..2061a1ced07 100644 --- a/integration/graphql-schema-first/src/main.ts +++ b/integration/graphql-schema-first/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/integration/graphql-schema-first/tsconfig.json b/integration/graphql-schema-first/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/graphql-schema-first/tsconfig.json +++ b/integration/graphql-schema-first/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/hello-world/e2e/exceptions.spec.ts b/integration/hello-world/e2e/exceptions.spec.ts index 581964807df..4b50db98219 100644 --- a/integration/hello-world/e2e/exceptions.spec.ts +++ b/integration/hello-world/e2e/exceptions.spec.ts @@ -4,10 +4,9 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { RawServerDefault } from 'fastify'; -import * as request from 'supertest'; -import { ErrorsController } from '../src/errors/errors.controller'; +import request from 'supertest'; +import { ErrorsController } from '../src/errors/errors.controller.js'; describe('Error messages', () => { let server: RawServerDefault; @@ -82,14 +81,12 @@ describe('Error messages', () => { url: '/sync', }) .then(({ payload, statusCode }) => { - expect(statusCode).to.equal(HttpStatus.BAD_REQUEST); - expect(payload).to.equal( - JSON.stringify({ - statusCode: 400, - error: 'Bad Request', - message: 'Integration test', - }), - ); + expect(statusCode).toBe(HttpStatus.BAD_REQUEST); + expect(JSON.parse(payload)).toEqual({ + statusCode: 400, + error: 'Bad Request', + message: 'Integration test', + }); }); }); @@ -97,17 +94,15 @@ describe('Error messages', () => { return app .inject({ method: 'GET', - url: '/sync', + url: '/async', }) .then(({ payload, statusCode }) => { - expect(statusCode).to.equal(HttpStatus.BAD_REQUEST); - expect(payload).to.equal( - JSON.stringify({ - statusCode: 400, - error: 'Bad Request', - message: 'Integration test', - }), - ); + expect(statusCode).toBe(HttpStatus.BAD_REQUEST); + expect(JSON.parse(payload)).toEqual({ + statusCode: 400, + error: 'Bad Request', + message: 'Integration test', + }); }); }); @@ -118,13 +113,11 @@ describe('Error messages', () => { url: '/unexpected-error', }) .then(({ payload, statusCode }) => { - expect(statusCode).to.equal(HttpStatus.INTERNAL_SERVER_ERROR); - expect(payload).to.equal( - JSON.stringify({ - statusCode: 500, - message: 'Internal server error', - }), - ); + expect(statusCode).toBe(HttpStatus.INTERNAL_SERVER_ERROR); + expect(JSON.parse(payload)).toEqual({ + statusCode: 500, + message: 'Internal server error', + }); }); }); diff --git a/integration/hello-world/e2e/exclude-middleware-fastify.spec.ts b/integration/hello-world/e2e/exclude-middleware-fastify.spec.ts index ab36bdb74ca..27fb5c46cb7 100644 --- a/integration/hello-world/e2e/exclude-middleware-fastify.spec.ts +++ b/integration/hello-world/e2e/exclude-middleware-fastify.spec.ts @@ -12,8 +12,8 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; const RETURN_VALUE = 'test'; const MIDDLEWARE_VALUE = 'middleware'; diff --git a/integration/hello-world/e2e/exclude-middleware.spec.ts b/integration/hello-world/e2e/exclude-middleware.spec.ts index e5f4befc628..9cc466b35e3 100644 --- a/integration/hello-world/e2e/exclude-middleware.spec.ts +++ b/integration/hello-world/e2e/exclude-middleware.spec.ts @@ -8,8 +8,8 @@ import { RequestMethod, } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; const RETURN_VALUE = 'test'; const MIDDLEWARE_VALUE = 'middleware'; diff --git a/integration/hello-world/e2e/express-instance.spec.ts b/integration/hello-world/e2e/express-instance.spec.ts index f035a42704d..f2504de350f 100644 --- a/integration/hello-world/e2e/express-instance.spec.ts +++ b/integration/hello-world/e2e/express-instance.spec.ts @@ -1,10 +1,10 @@ import { INestApplication } from '@nestjs/common'; import { ExpressAdapter } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; -import * as express from 'express'; -import * as request from 'supertest'; +import express from 'express'; +import request from 'supertest'; import { App } from 'supertest/types'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; describe('Hello world (express instance)', () => { let server: App; diff --git a/integration/hello-world/e2e/express-multiple.spec.ts b/integration/hello-world/e2e/express-multiple.spec.ts index 1f799d47930..329450c93b3 100644 --- a/integration/hello-world/e2e/express-multiple.spec.ts +++ b/integration/hello-world/e2e/express-multiple.spec.ts @@ -1,9 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { ExpressAdapter } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; -import * as express from 'express'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import express from 'express'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Hello world (express instance with multiple applications)', () => { let server; diff --git a/integration/hello-world/e2e/fastify-adapter.spec.ts b/integration/hello-world/e2e/fastify-adapter.spec.ts index 73852ba2343..8d2f70c49fd 100644 --- a/integration/hello-world/e2e/fastify-adapter.spec.ts +++ b/integration/hello-world/e2e/fastify-adapter.spec.ts @@ -3,8 +3,7 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; describe('Hello world (fastify adapter)', () => { let app: NestFastifyApplication; @@ -26,7 +25,10 @@ describe('Hello world (fastify adapter)', () => { method: 'GET', url: '/hello', }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); + .then(({ payload, statusCode }) => { + expect(statusCode).toBe(200); + expect(payload).toEqual('Hello world!'); + }); }); it(`/GET (Promise/async)`, () => { @@ -35,7 +37,10 @@ describe('Hello world (fastify adapter)', () => { method: 'GET', url: '/hello/async', }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); + .then(({ payload, statusCode }) => { + expect(statusCode).toBe(200); + expect(payload).toEqual('Hello world!'); + }); }); it(`/GET (Observable stream)`, () => { @@ -44,7 +49,10 @@ describe('Hello world (fastify adapter)', () => { method: 'GET', url: '/hello/stream', }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); + .then(({ payload, statusCode }) => { + expect(statusCode).toBe(200); + expect(payload).toEqual('Hello world!'); + }); }); it(`/GET { host: ":tenant.example.com" } not matched`, () => { @@ -54,7 +62,7 @@ describe('Hello world (fastify adapter)', () => { url: '/host', }) .then(({ payload }) => { - expect(JSON.parse(payload)).to.be.eql({ + expect(JSON.parse(payload)).toEqual({ error: 'Internal Server Error', message: 'HTTP adapter does not support filtering on host: ":tenant.example.com"', @@ -70,7 +78,7 @@ describe('Hello world (fastify adapter)', () => { url: '/host-array', }) .then(({ payload }) => { - expect(JSON.parse(payload)).to.be.eql({ + expect(JSON.parse(payload)).toEqual({ error: 'Internal Server Error', message: 'HTTP adapter does not support filtering on hosts: [":tenant.example1.com", ":tenant.example2.com"]', @@ -84,7 +92,10 @@ describe('Hello world (fastify adapter)', () => { .inject() .get('/hello') .end() - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); + .then(({ payload, statusCode }) => { + expect(statusCode).toBe(200); + expect(payload).toEqual('Hello world!'); + }); }); it('/HEAD should respond to with a 200', () => { @@ -93,7 +104,7 @@ describe('Hello world (fastify adapter)', () => { method: 'HEAD', url: '/hello', }) - .then(({ statusCode }) => expect(statusCode).to.be.eq(200)); + .then(({ statusCode }) => expect(statusCode).toBe(200)); }); afterEach(async () => { diff --git a/integration/hello-world/e2e/fastify-middleware-before-init.spec.ts b/integration/hello-world/e2e/fastify-middleware-before-init.spec.ts index cf22878852e..dd808dae120 100644 --- a/integration/hello-world/e2e/fastify-middleware-before-init.spec.ts +++ b/integration/hello-world/e2e/fastify-middleware-before-init.spec.ts @@ -11,7 +11,6 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; describe('Middleware before init (FastifyAdapter)', () => { let app: NestFastifyApplication; @@ -81,11 +80,11 @@ describe('Middleware before init (FastifyAdapter)', () => { url: '/test', }) .then(({ statusCode, payload, headers }) => { - expect(statusCode).to.equal(200); - expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' }); + expect(statusCode).toBe(200); + expect(JSON.parse(payload)).toEqual({ data: 'test_data' }); // Verify both module-level and global middleware were applied - expect(headers['x-middleware']).to.equal('applied'); - expect(headers['x-global-middleware']).to.equal('applied'); + expect(headers['x-middleware']).toBe('applied'); + expect(headers['x-global-middleware']).toBe('applied'); }); }); @@ -123,8 +122,8 @@ describe('Middleware before init (FastifyAdapter)', () => { url: '/test', }) .then(({ statusCode, payload }) => { - expect(statusCode).to.equal(200); - expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' }); + expect(statusCode).toBe(200); + expect(JSON.parse(payload)).toEqual({ data: 'test_data' }); }); }); diff --git a/integration/hello-world/e2e/fastify-multiple.spec.ts b/integration/hello-world/e2e/fastify-multiple.spec.ts index d8dc60b1043..64f8d3eb0c8 100644 --- a/integration/hello-world/e2e/fastify-multiple.spec.ts +++ b/integration/hello-world/e2e/fastify-multiple.spec.ts @@ -1,91 +1,4 @@ -/* Temporarily disabled due to various regressions - -import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; -import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { ApplicationModule } from '../src/app.module'; - -describe('Hello world (fastify adapter with multiple applications)', () => { - let adapter: FastifyAdapter; - let apps: NestFastifyApplication[]; - - beforeEach(async () => { - const module1 = await Test.createTestingModule({ - imports: [ApplicationModule], - }).compile(); - const module2 = await Test.createTestingModule({ - imports: [ApplicationModule], - }).compile(); - - adapter = new FastifyAdapter(); - - apps = [ - module1.createNestApplication(adapter), - module2 - .createNestApplication(adapter, { - bodyParser: false, - }) - .setGlobalPrefix('/app2'), - ]; - await Promise.all(apps.map(app => app.init())); - }); - - it(`/GET`, () => { - return adapter - .inject({ - method: 'GET', - url: '/hello', - }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); - }); - - it(`/GET (app2)`, () => { - return adapter - .inject({ - method: 'GET', - url: '/app2/hello', - }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); - }); - - it(`/GET (Promise/async)`, () => { - return adapter - .inject({ - method: 'GET', - url: '/hello/async', - }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); - }); - - it(`/GET (app2 Promise/async)`, () => { - return adapter - .inject({ - method: 'GET', - url: '/app2/hello/async', - }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); - }); - - it(`/GET (Observable stream)`, () => { - return adapter - .inject({ - method: 'GET', - url: '/hello/stream', - }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); - }); - - it(`/GET (app2 Observable stream)`, () => { - return adapter - .inject({ - method: 'GET', - url: '/app2/hello/stream', - }) - .then(({ payload }) => expect(payload).to.be.eql('Hello world!')); - }); - - afterEach(async () => { - await Promise.all(apps.map(app => app.close())); - await adapter.close(); - }); -});*/ +// Temporarily disabled due to various regressions +describe.skip('Hello world (fastify adapter with multiple applications)', () => { + it('placeholder', () => {}); +}); diff --git a/integration/hello-world/e2e/force-console.spec.ts b/integration/hello-world/e2e/force-console.spec.ts index cb6aa733baa..9c6f687fa7b 100644 --- a/integration/hello-world/e2e/force-console.spec.ts +++ b/integration/hello-world/e2e/force-console.spec.ts @@ -1,25 +1,23 @@ import { ConsoleLogger, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; -import * as sinon from 'sinon'; -import { expect } from 'chai'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('ForceConsole Option', () => { let app: INestApplication; describe('When forceConsole is true', () => { - let consoleLogSpy: sinon.SinonSpy; - let consoleErrorSpy: sinon.SinonSpy; - let processStdoutSpy: sinon.SinonSpy; - let processStderrSpy: sinon.SinonSpy; + let consoleLogSpy: ReturnType; + let consoleErrorSpy: ReturnType; + let processStdoutSpy: ReturnType; + let processStderrSpy: ReturnType; beforeEach(async () => { // Spy on console and process methods - consoleLogSpy = sinon.spy(console, 'log'); - consoleErrorSpy = sinon.spy(console, 'error'); - processStdoutSpy = sinon.spy(process.stdout, 'write'); - processStderrSpy = sinon.spy(process.stderr, 'write'); + consoleLogSpy = vi.spyOn(console, 'log'); + consoleErrorSpy = vi.spyOn(console, 'error'); + processStdoutSpy = vi.spyOn(process.stdout, 'write'); + processStderrSpy = vi.spyOn(process.stderr, 'write'); const moduleRef = await Test.createTestingModule({ imports: [AppModule], @@ -34,10 +32,10 @@ describe('ForceConsole Option', () => { }); afterEach(async () => { - consoleLogSpy.restore(); - consoleErrorSpy.restore(); - processStdoutSpy.restore(); - processStderrSpy.restore(); + consoleLogSpy.mockRestore(); + consoleErrorSpy.mockRestore(); + processStdoutSpy.mockRestore(); + processStderrSpy.mockRestore(); await app.close(); }); @@ -46,14 +44,12 @@ describe('ForceConsole Option', () => { logger.log('Test log message'); // Should use console.log when forceConsole is true - expect(consoleLogSpy.called).to.be.true; + expect(consoleLogSpy).toHaveBeenCalled(); // Verify console.log was called with the message - const consoleLogCalls = consoleLogSpy - .getCalls() - .filter(call => - call.args.some(arg => String(arg).includes('Test log message')), - ); - expect(consoleLogCalls.length).to.be.greaterThan(0); + const consoleLogCalls = consoleLogSpy.mock.calls.filter(args => + args.some(arg => String(arg).includes('Test log message')), + ); + expect(consoleLogCalls.length).toBeGreaterThan(0); }); it('should use console.error instead of process.stderr.write', async () => { @@ -61,14 +57,12 @@ describe('ForceConsole Option', () => { logger.error('Test error message'); // Should use console.error when forceConsole is true - expect(consoleErrorSpy.called).to.be.true; + expect(consoleErrorSpy).toHaveBeenCalled(); // Verify console.error was called with the message - const consoleErrorCalls = consoleErrorSpy - .getCalls() - .filter(call => - call.args.some(arg => String(arg).includes('Test error message')), - ); - expect(consoleErrorCalls.length).to.be.greaterThan(0); + const consoleErrorCalls = consoleErrorSpy.mock.calls.filter(args => + args.some(arg => String(arg).includes('Test error message')), + ); + expect(consoleErrorCalls.length).toBeGreaterThan(0); }); it('should handle GET request with forceConsole option enabled', () => { @@ -77,17 +71,17 @@ describe('ForceConsole Option', () => { }); describe('When forceConsole is false (default)', () => { - let consoleLogSpy: sinon.SinonSpy; - let consoleErrorSpy: sinon.SinonSpy; - let processStdoutSpy: sinon.SinonSpy; - let processStderrSpy: sinon.SinonSpy; + let consoleLogSpy: ReturnType; + let consoleErrorSpy: ReturnType; + let processStdoutSpy: ReturnType; + let processStderrSpy: ReturnType; beforeEach(async () => { // Spy on console and process methods - consoleLogSpy = sinon.spy(console, 'log'); - consoleErrorSpy = sinon.spy(console, 'error'); - processStdoutSpy = sinon.spy(process.stdout, 'write'); - processStderrSpy = sinon.spy(process.stderr, 'write'); + consoleLogSpy = vi.spyOn(console, 'log'); + consoleErrorSpy = vi.spyOn(console, 'error'); + processStdoutSpy = vi.spyOn(process.stdout, 'write'); + processStderrSpy = vi.spyOn(process.stderr, 'write'); const moduleRef = await Test.createTestingModule({ imports: [AppModule], @@ -102,10 +96,10 @@ describe('ForceConsole Option', () => { }); afterEach(async () => { - consoleLogSpy.restore(); - consoleErrorSpy.restore(); - processStdoutSpy.restore(); - processStderrSpy.restore(); + consoleLogSpy.mockRestore(); + consoleErrorSpy.mockRestore(); + processStdoutSpy.mockRestore(); + processStderrSpy.mockRestore(); await app.close(); }); @@ -113,31 +107,31 @@ describe('ForceConsole Option', () => { const logger = new ConsoleLogger('TestContext'); // Reset spy to ensure clean state - consoleLogSpy.resetHistory(); + consoleLogSpy.mockClear(); logger.log('Test log message'); // When forceConsole is false, should not call console.log - expect(consoleLogSpy.called).to.be.false; + expect(consoleLogSpy).not.toHaveBeenCalled(); }); it('should not directly call console.error when forceConsole is false', async () => { const logger = new ConsoleLogger('TestContext'); // Reset spy to ensure clean state - consoleErrorSpy.resetHistory(); + consoleErrorSpy.mockClear(); logger.error('Test error message'); // When forceConsole is false, should not call console.error - expect(consoleErrorSpy.called).to.be.false; + expect(consoleErrorSpy).not.toHaveBeenCalled(); }); }); describe('When forceConsole is set via NestFactory.create', () => { it('should apply forceConsole to the default logger', async () => { - const consoleLogSpy = sinon.spy(console, 'log'); - const processStdoutSpy = sinon.spy(process.stdout, 'write'); + const consoleLogSpy = vi.spyOn(console, 'log'); + const processStdoutSpy = vi.spyOn(process.stdout, 'write'); const moduleRef = await Test.createTestingModule({ imports: [AppModule], @@ -153,10 +147,10 @@ describe('ForceConsole Option', () => { const logger = new ConsoleLogger('AppContext', { forceConsole: true }); logger.log('Application started'); - expect(consoleLogSpy.called).to.be.true; + expect(consoleLogSpy).toHaveBeenCalled(); - consoleLogSpy.restore(); - processStdoutSpy.restore(); + consoleLogSpy.mockRestore(); + processStdoutSpy.mockRestore(); await testApp.close(); }); }); diff --git a/integration/hello-world/e2e/guards.spec.ts b/integration/hello-world/e2e/guards.spec.ts index 97082612389..db362ba444d 100644 --- a/integration/hello-world/e2e/guards.spec.ts +++ b/integration/hello-world/e2e/guards.spec.ts @@ -5,8 +5,8 @@ import { } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; @Injectable() export class AuthGuard { @@ -33,10 +33,31 @@ function createTestModule(guard) { describe('Guards', () => { let app: INestApplication; + afterEach(async () => { + await app.close(); + }); + it(`should prevent access (unauthorized)`, async () => { app = (await createTestModule(new AuthGuard())).createNestApplication(); await app.init(); - return request(app.getHttpServer()).get('/hello').expect(401); + return request(app.getHttpServer()) + .get('/hello') + .expect(401) + .expect(({ body }) => { + expect(body.message).toBe('Unauthorized'); + expect(body.statusCode).toBe(401); + }); + }); + + it(`should allow access when guard returns true`, async () => { + const allowGuard = { canActivate: () => true }; + app = (await createTestModule(allowGuard)).createNestApplication(); + + await app.init(); + return request(app.getHttpServer()) + .get('/hello') + .expect(200) + .expect('Hello world!'); }); }); diff --git a/integration/hello-world/e2e/hello-world.spec.ts b/integration/hello-world/e2e/hello-world.spec.ts index e2f3a895f52..c828a49d85e 100644 --- a/integration/hello-world/e2e/hello-world.spec.ts +++ b/integration/hello-world/e2e/hello-world.spec.ts @@ -1,7 +1,7 @@ -import * as request from 'supertest'; +import request from 'supertest'; import { Test } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; describe('Hello world (default adapter)', () => { let server; diff --git a/integration/hello-world/e2e/interceptors.spec.ts b/integration/hello-world/e2e/interceptors.spec.ts index 249eb9312aa..5da20a73ffd 100644 --- a/integration/hello-world/e2e/interceptors.spec.ts +++ b/integration/hello-world/e2e/interceptors.spec.ts @@ -9,8 +9,8 @@ import { APP_INTERCEPTOR } from '@nestjs/core'; import { Test } from '@nestjs/testing'; import { of } from 'rxjs'; import { map } from 'rxjs/operators'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; const RETURN_VALUE = 'test'; diff --git a/integration/hello-world/e2e/local-pipes.spec.ts b/integration/hello-world/e2e/local-pipes.spec.ts index e982bc4aecc..63cfeed5a05 100644 --- a/integration/hello-world/e2e/local-pipes.spec.ts +++ b/integration/hello-world/e2e/local-pipes.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Hello world (default adapter)', () => { let server; diff --git a/integration/hello-world/e2e/middleware-class.spec.ts b/integration/hello-world/e2e/middleware-class.spec.ts index 49896423608..2905e60d284 100644 --- a/integration/hello-world/e2e/middleware-class.spec.ts +++ b/integration/hello-world/e2e/middleware-class.spec.ts @@ -9,8 +9,8 @@ import { } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { Response } from 'express'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; const INCLUDED_VALUE = 'test_included'; const RETURN_VALUE = 'test'; diff --git a/integration/hello-world/e2e/middleware-execute-order.spec.ts b/integration/hello-world/e2e/middleware-execute-order.spec.ts index 21de0245246..7e660be19b2 100644 --- a/integration/hello-world/e2e/middleware-execute-order.spec.ts +++ b/integration/hello-world/e2e/middleware-execute-order.spec.ts @@ -5,7 +5,7 @@ import { Module, } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; +import request from 'supertest'; const RETURN_VALUE_A = 'test_A'; const RETURN_VALUE_B = 'test_B'; diff --git a/integration/hello-world/e2e/middleware-fastify.spec.ts b/integration/hello-world/e2e/middleware-fastify.spec.ts index 98ee4676078..ad2ce09b933 100644 --- a/integration/hello-world/e2e/middleware-fastify.spec.ts +++ b/integration/hello-world/e2e/middleware-fastify.spec.ts @@ -15,10 +15,9 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { FastifyRequest } from 'fastify'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Middleware (FastifyAdapter)', () => { let app: NestFastifyApplication; @@ -117,7 +116,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'GET', url: '/hello', }) - .then(({ payload }) => expect(payload).to.be.eql(RETURN_VALUE)); + .then(({ payload }) => expect(payload).toEqual(RETURN_VALUE)); }); it(`forRoutes(TestController)`, () => { @@ -126,7 +125,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'GET', url: '/test', }) - .then(({ payload }) => expect(payload).to.be.eql(SCOPED_VALUE)); + .then(({ payload }) => expect(payload).toEqual(SCOPED_VALUE)); }); it(`query?test=${QUERY_VALUE} forRoutes(query)`, () => { @@ -138,7 +137,7 @@ describe('Middleware (FastifyAdapter)', () => { test: QUERY_VALUE, }, }) - .then(({ payload }) => expect(payload).to.be.eql(QUERY_VALUE)); + .then(({ payload }) => expect(payload).toEqual(QUERY_VALUE)); }); it(`${QUERY_VALUE}?test=${QUERY_VALUE} forRoutes(${QUERY_VALUE})`, () => { @@ -150,7 +149,7 @@ describe('Middleware (FastifyAdapter)', () => { test: QUERY_VALUE, }, }) - .then(({ payload }) => expect(payload).to.be.eql(QUERY_VALUE)); + .then(({ payload }) => expect(payload).toEqual(QUERY_VALUE)); }); it(`forRoutes(tests/*path)`, () => { @@ -159,7 +158,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'GET', url: '/tests/wildcard_nested', }) - .then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE)); + .then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE)); }); it(`forRoutes(express_style_wildcard/*)`, () => { @@ -168,7 +167,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'GET', url: '/express_style_wildcard/wildcard_nested', }) - .then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE)); + .then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE)); }); it(`forRoutes(legacy_style_wildcard/*)`, () => { @@ -177,7 +176,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'GET', url: '/legacy_style_wildcard/wildcard_nested', }) - .then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE)); + .then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE)); }); it(`forRoutes(req/url/)`, () => { @@ -187,7 +186,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'GET', url: `/req/url${reqUrl}`, }) - .then(({ payload }) => expect(payload).to.be.eql(REQ_URL_VALUE)); + .then(({ payload }) => expect(payload).toEqual(REQ_URL_VALUE)); }); it(`GET forRoutes(POST tests/included)`, () => { @@ -196,7 +195,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'GET', url: '/tests/included', }) - .then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE)); + .then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE)); }); it(`POST forRoutes(POST tests/included)`, () => { @@ -205,7 +204,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'POST', url: '/tests/included', }) - .then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE)); + .then(({ payload }) => expect(payload).toEqual(INCLUDED_VALUE)); }); it(`GET forRoutes(POST /tests/%69ncluded) - ensure middleware is executed correctly with encoded characters`, () => { @@ -214,7 +213,7 @@ describe('Middleware (FastifyAdapter)', () => { method: 'POST', url: '/tests/%69ncluded', // 'i' character is encoded }) - .then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE)); + .then(({ payload }) => expect(payload).toEqual(INCLUDED_VALUE)); }); afterEach(async () => { @@ -329,7 +328,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/a/b/c', }) .then(({ payload }) => { - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, actual: 1, @@ -346,7 +345,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/a/b', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, actual: 1, @@ -363,7 +362,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/a', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, actual: 1, @@ -380,7 +379,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/similar', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, actual: 1, @@ -397,7 +396,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/similar/test', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, actual: 1, @@ -414,7 +413,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/similar/arbitrary', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, actual: 1, @@ -494,7 +493,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/api/pong', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, pong: 'pong', @@ -513,7 +512,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/api', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, pong: 'pong', @@ -531,7 +530,7 @@ describe('Middleware (FastifyAdapter)', () => { url: '/pong', }) .then(({ payload }) => - expect(payload).to.be.eql( + expect(payload).toEqual( JSON.stringify({ success: true, pong: 'pong', diff --git a/integration/hello-world/e2e/middleware-run-match-route.ts b/integration/hello-world/e2e/middleware-run-match-route.ts index c3c8ed3cfae..1e94d19b0bf 100644 --- a/integration/hello-world/e2e/middleware-run-match-route.ts +++ b/integration/hello-world/e2e/middleware-run-match-route.ts @@ -7,10 +7,8 @@ import { NestMiddleware, Module, } from '@nestjs/common'; -import { Test } from '../../../packages/testing'; -import * as request from 'supertest'; -import { expect } from 'chai'; - +import { Test } from '../../../packages/testing.js'; +import request from 'supertest'; /** * Number of times that the middleware was executed. */ diff --git a/integration/hello-world/e2e/middleware-with-versioning.spec.ts b/integration/hello-world/e2e/middleware-with-versioning.spec.ts index a16645b6859..f5f77616993 100644 --- a/integration/hello-world/e2e/middleware-with-versioning.spec.ts +++ b/integration/hello-world/e2e/middleware-with-versioning.spec.ts @@ -6,14 +6,14 @@ import { Module, RequestMethod, Version, + VERSION_NEUTRAL, VersioningOptions, VersioningType, - VERSION_NEUTRAL, } from '@nestjs/common'; -import { CustomVersioningOptions } from '@nestjs/common/interfaces'; +import { CustomVersioningOptions } from '@nestjs/common/interfaces/index.js'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; const RETURN_VALUE = 'test'; const VERSIONED_VALUE = 'test_versioned'; diff --git a/integration/hello-world/e2e/middleware.spec.ts b/integration/hello-world/e2e/middleware.spec.ts index 6d349d83162..7412de91f23 100644 --- a/integration/hello-world/e2e/middleware.spec.ts +++ b/integration/hello-world/e2e/middleware.spec.ts @@ -6,8 +6,8 @@ import { Module, } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; const RETURN_VALUE = 'test'; const SCOPED_VALUE = 'test_scoped'; diff --git a/integration/hello-world/e2e/router-module-middleware.spec.ts b/integration/hello-world/e2e/router-module-middleware.spec.ts index 52fe55ced5f..ab52b4e5cdd 100644 --- a/integration/hello-world/e2e/router-module-middleware.spec.ts +++ b/integration/hello-world/e2e/router-module-middleware.spec.ts @@ -7,8 +7,8 @@ import { } from '@nestjs/common'; import { RouterModule } from '@nestjs/core'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; const RETURN_VALUE = 'test'; const SCOPED_VALUE = 'test_scoped'; diff --git a/integration/hello-world/e2e/router-module.spec.ts b/integration/hello-world/e2e/router-module.spec.ts index cc576e261e5..43ffc6451bf 100644 --- a/integration/hello-world/e2e/router-module.spec.ts +++ b/integration/hello-world/e2e/router-module.spec.ts @@ -1,7 +1,7 @@ import { Controller, Get, INestApplication, Module } from '@nestjs/common'; import { RouterModule, Routes } from '@nestjs/core'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; +import request from 'supertest'; describe('RouterModule', () => { let app: INestApplication; @@ -66,7 +66,7 @@ describe('RouterModule', () => { }) class AppModule {} - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [MainModule, AppModule], }).compile(); diff --git a/integration/hello-world/e2e/schema-in-pipes.spec.ts b/integration/hello-world/e2e/schema-in-pipes.spec.ts new file mode 100644 index 00000000000..82db9a835a5 --- /dev/null +++ b/integration/hello-world/e2e/schema-in-pipes.spec.ts @@ -0,0 +1,166 @@ +import { + ArgumentMetadata, + Body, + Controller, + createParamDecorator, + ExecutionContext, + Get, + INestApplication, + Injectable, + Module, + Param, + PipeTransform, + Post, + Query, +} from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import request from 'supertest'; + +const testSchema: StandardSchemaV1 = { + '~standard': { + version: 1, + vendor: 'test', + validate: (value: unknown) => ({ value }), + }, +}; + +/** + * A pipe that captures the ArgumentMetadata it receives, + * so the test can verify that `schema` is propagated. + */ +@Injectable() +class SchemaCaptorPipe implements PipeTransform { + static lastMetadata: ArgumentMetadata | undefined; + + transform(value: any, metadata: ArgumentMetadata) { + SchemaCaptorPipe.lastMetadata = metadata; + return value; + } +} + +const CustomParam = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + return ctx.switchToHttp().getRequest().query; + }, +); + +@Controller('schema-test') +class SchemaTestController { + @Post('body') + bodyWithSchema( + @Body({ schema: testSchema, pipes: [SchemaCaptorPipe] }) body: any, + ) { + return { received: body }; + } + + @Get('query') + queryWithSchema( + @Query({ schema: testSchema, pipes: [SchemaCaptorPipe] }) query: any, + ) { + return { received: query }; + } + + @Get('param/:id') + paramWithSchema( + @Param('id', { schema: testSchema, pipes: [SchemaCaptorPipe] }) id: string, + ) { + return { received: id }; + } + + @Get('custom') + customWithSchema( + @CustomParam({ schema: testSchema, pipes: [SchemaCaptorPipe] }) value: any, + ) { + return { received: value }; + } + + @Post('body-property') + bodyPropertyWithSchema( + @Body('name', { schema: testSchema, pipes: [SchemaCaptorPipe] }) + name: string, + ) { + return { received: name }; + } +} + +@Module({ + controllers: [SchemaTestController], + providers: [SchemaCaptorPipe], +}) +class SchemaTestModule {} + +describe('Schema propagation to pipes', () => { + let app: INestApplication; + let server: any; + + beforeAll(async () => { + const module = await Test.createTestingModule({ + imports: [SchemaTestModule], + }).compile(); + + app = module.createNestApplication(); + server = app.getHttpServer(); + await app.init(); + }); + + beforeEach(() => { + SchemaCaptorPipe.lastMetadata = undefined; + }); + + afterAll(async () => { + await app.close(); + }); + + it('should pass schema to pipe via @Body(options)', async () => { + await request(server) + .post('/schema-test/body') + .send({ name: 'test' }) + .expect(201); + + expect(SchemaCaptorPipe.lastMetadata).toBeDefined(); + expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema); + expect(SchemaCaptorPipe.lastMetadata!.type).toBe('body'); + }); + + it('should pass schema to pipe via @Query(options)', async () => { + await request(server).get('/schema-test/query?user=john').expect(200); + + expect(SchemaCaptorPipe.lastMetadata).toBeDefined(); + expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema); + expect(SchemaCaptorPipe.lastMetadata!.type).toBe('query'); + }); + + it('should pass schema to pipe via @Param(property, options)', async () => { + await request(server) + .get('/schema-test/param/42') + .expect(200) + .expect({ received: '42' }); + + expect(SchemaCaptorPipe.lastMetadata).toBeDefined(); + expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema); + expect(SchemaCaptorPipe.lastMetadata!.type).toBe('param'); + expect(SchemaCaptorPipe.lastMetadata!.data).toBe('id'); + }); + + it('should pass schema to pipe via createParamDecorator(options)', async () => { + await request(server).get('/schema-test/custom?key=val').expect(200); + + expect(SchemaCaptorPipe.lastMetadata).toBeDefined(); + expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema); + expect(SchemaCaptorPipe.lastMetadata!.type).toBe('custom'); + }); + + it('should pass schema to pipe via @Body(property, options)', async () => { + await request(server) + .post('/schema-test/body-property') + .send({ name: 'Alice' }) + .expect(201) + .expect({ received: 'Alice' }); + + expect(SchemaCaptorPipe.lastMetadata).toBeDefined(); + expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema); + expect(SchemaCaptorPipe.lastMetadata!.type).toBe('body'); + expect(SchemaCaptorPipe.lastMetadata!.data).toBe('name'); + }); +}); diff --git a/integration/hello-world/e2e/standard-schema-serializer.spec.ts b/integration/hello-world/e2e/standard-schema-serializer.spec.ts new file mode 100644 index 00000000000..d5b51710be1 --- /dev/null +++ b/integration/hello-world/e2e/standard-schema-serializer.spec.ts @@ -0,0 +1,316 @@ +import { + Controller, + Get, + INestApplication, + Module, + SerializeOptions, + StandardSchemaSerializerInterceptor, + UseInterceptors, +} from '@nestjs/common'; +import { APP_INTERCEPTOR, Reflector } from '@nestjs/core'; +import { Test } from '@nestjs/testing'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import request from 'supertest'; + +// ─── Test schemas ────────────────────────────────────────────── + +/** + * Schema that strips out the `password` field (simulating a "safe user" DTO). + */ +const safeUserSchema: StandardSchemaV1 = { + '~standard': { + version: 1, + vendor: 'test', + validate: (value: unknown) => { + const { password, ...safe } = value as Record; + return { value: safe }; + }, + }, +}; + +/** + * Schema that adds a `serialized: true` flag (useful for asserting the schema ran). + */ +const flagSchema: StandardSchemaV1 = { + '~standard': { + version: 1, + vendor: 'test', + validate: (value: unknown) => ({ + value: { ...(value as any), serialized: true }, + }), + }, +}; + +/** + * Schema that always fails — used for the error case. + */ +const failingSchema: StandardSchemaV1 = { + '~standard': { + version: 1, + vendor: 'test', + validate: () => ({ + issues: [{ message: 'not allowed' }], + }), + }, +}; + +/** + * Async schema — validates that the interceptor awaits promises. + */ +const asyncSchema: StandardSchemaV1 = { + '~standard': { + version: 1, + vendor: 'test', + validate: async (value: unknown) => ({ + value: { ...(value as any), async: true }, + }), + }, +}; + +// ─── Controllers ─────────────────────────────────────────────── + +@Controller('serializer') +@UseInterceptors(StandardSchemaSerializerInterceptor) +class SerializerTestController { + @Get('user') + @SerializeOptions({ schema: safeUserSchema }) + getUser() { + return { id: 1, name: 'Alice', password: 'secret123' }; + } + + @Get('users') + @SerializeOptions({ schema: safeUserSchema }) + getUsers() { + return [ + { id: 1, name: 'Alice', password: 'pw1' }, + { id: 2, name: 'Bob', password: 'pw2' }, + ]; + } + + @Get('flagged') + @SerializeOptions({ schema: flagSchema }) + getFlagged() { + return { id: 1 }; + } + + @Get('no-schema') + getNoSchema() { + return { id: 1, secret: 'visible' }; + } + + @Get('failing') + @SerializeOptions({ schema: failingSchema }) + getFailing() { + return { id: 1 }; + } + + @Get('async') + @SerializeOptions({ schema: asyncSchema }) + getAsync() { + return { id: 1 }; + } + + @Get('primitive') + @SerializeOptions({ schema: failingSchema }) + getPrimitive() { + return 'plain string'; + } +} + +/** + * Controller-level schema applied via class decorator — all routes inherit it. + */ +@Controller('class-level') +@UseInterceptors(StandardSchemaSerializerInterceptor) +@SerializeOptions({ schema: safeUserSchema }) +class ClassLevelSerializerController { + @Get('user') + getUser() { + return { id: 1, name: 'Carol', password: 'secret' }; + } + + @Get('override') + @SerializeOptions({ schema: flagSchema }) + getOverride() { + return { id: 1, name: 'Carol', password: 'secret' }; + } +} + +/** + * Controller demonstrating global interceptor registration with a default schema. + */ +@Controller('global') +class GlobalSerializerController { + @Get('default') + getDefault() { + return { id: 1, name: 'Dave', password: 'global-secret' }; + } + + @Get('override') + @SerializeOptions({ schema: flagSchema }) + getOverride() { + return { id: 1 }; + } +} + +@Module({ + controllers: [SerializerTestController, ClassLevelSerializerController], +}) +class SerializerTestModule {} + +@Module({ + controllers: [GlobalSerializerController], + providers: [ + { + provide: APP_INTERCEPTOR, + useFactory: (reflector: Reflector) => + new StandardSchemaSerializerInterceptor(reflector, { + schema: safeUserSchema, + }), + inject: [Reflector], + }, + ], +}) +class GlobalSerializerTestModule {} + +// ─── Tests ───────────────────────────────────────────────────── + +describe('StandardSchemaSerializerInterceptor (integration)', () => { + let app: INestApplication; + + afterEach(async () => { + await app.close(); + }); + + describe('handler-level @SerializeOptions', () => { + beforeEach(async () => { + const module = await Test.createTestingModule({ + imports: [SerializerTestModule], + }).compile(); + + app = module.createNestApplication(); + await app.init(); + }); + + it('should strip fields via schema on a single object', () => { + return request(app.getHttpServer()) + .get('/serializer/user') + .expect(200) + .expect(({ body }) => { + expect(body).toEqual({ id: 1, name: 'Alice' }); + expect(body).not.toHaveProperty('password'); + }); + }); + + it('should apply schema to each item in an array response', () => { + return request(app.getHttpServer()) + .get('/serializer/users') + .expect(200) + .expect(({ body }) => { + expect(body).toEqual([ + { id: 1, name: 'Alice' }, + { id: 2, name: 'Bob' }, + ]); + body.forEach((item: any) => + expect(item).not.toHaveProperty('password'), + ); + }); + }); + + it('should augment response through the schema', () => { + return request(app.getHttpServer()) + .get('/serializer/flagged') + .expect(200) + .expect({ id: 1, serialized: true }); + }); + + it('should return response unchanged when no schema is set', () => { + return request(app.getHttpServer()) + .get('/serializer/no-schema') + .expect(200) + .expect({ id: 1, secret: 'visible' }); + }); + + it('should return 500 when schema validation fails', () => { + return request(app.getHttpServer()) + .get('/serializer/failing') + .expect(500); + }); + + it('should handle async schemas', () => { + return request(app.getHttpServer()) + .get('/serializer/async') + .expect(200) + .expect({ id: 1, async: true }); + }); + + it('should pass primitive values through even when a schema is set', () => { + return request(app.getHttpServer()) + .get('/serializer/primitive') + .expect(200) + .expect(({ text }) => { + expect(text).toBe('plain string'); + }); + }); + }); + + describe('class-level @SerializeOptions', () => { + beforeEach(async () => { + const module = await Test.createTestingModule({ + imports: [SerializerTestModule], + }).compile(); + + app = module.createNestApplication(); + await app.init(); + }); + + it('should apply class-level schema to all routes', () => { + return request(app.getHttpServer()) + .get('/class-level/user') + .expect(200) + .expect(({ body }) => { + expect(body).toEqual({ id: 1, name: 'Carol' }); + expect(body).not.toHaveProperty('password'); + }); + }); + + it('should allow handler-level schema to override class-level', () => { + return request(app.getHttpServer()) + .get('/class-level/override') + .expect(200) + .expect(({ body }) => { + // flagSchema adds `serialized: true` but does NOT strip password + expect(body).toHaveProperty('serialized', true); + expect(body).toHaveProperty('password', 'secret'); + }); + }); + }); + + describe('global interceptor with default schema', () => { + beforeEach(async () => { + const module = await Test.createTestingModule({ + imports: [GlobalSerializerTestModule], + }).compile(); + + app = module.createNestApplication(); + await app.init(); + }); + + it('should apply the default schema globally', () => { + return request(app.getHttpServer()) + .get('/global/default') + .expect(200) + .expect(({ body }) => { + expect(body).toEqual({ id: 1, name: 'Dave' }); + expect(body).not.toHaveProperty('password'); + }); + }); + + it('should let @SerializeOptions override the global default', () => { + return request(app.getHttpServer()) + .get('/global/override') + .expect(200) + .expect({ id: 1, serialized: true }); + }); + }); +}); diff --git a/integration/hello-world/src/app.module.ts b/integration/hello-world/src/app.module.ts index b34bf1cfe84..023aa35fb92 100644 --- a/integration/hello-world/src/app.module.ts +++ b/integration/hello-world/src/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { HelloModule } from './hello/hello.module'; -import { HostArrayModule } from './host-array/host-array.module'; -import { HostModule } from './host/host.module'; +import { HelloModule } from './hello/hello.module.js'; +import { HostArrayModule } from './host-array/host-array.module.js'; +import { HostModule } from './host/host.module.js'; @Module({ imports: [HelloModule, HostModule, HostArrayModule], diff --git a/integration/hello-world/src/hello/hello.controller.ts b/integration/hello-world/src/hello/hello.controller.ts index be715e1471b..5db2d29d460 100644 --- a/integration/hello-world/src/hello/hello.controller.ts +++ b/integration/hello-world/src/hello/hello.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Header, Param } from '@nestjs/common'; import { Observable, of } from 'rxjs'; -import { HelloService } from './hello.service'; -import { UserByIdPipe } from './users/user-by-id.pipe'; +import { HelloService } from './hello.service.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; @Controller('hello') export class HelloController { diff --git a/integration/hello-world/src/hello/hello.module.ts b/integration/hello-world/src/hello/hello.module.ts index b831a931577..fe28640f84f 100644 --- a/integration/hello-world/src/hello/hello.module.ts +++ b/integration/hello-world/src/hello/hello.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloService } from './hello.service'; -import { UsersService } from './users/users.service'; +import { HelloController } from './hello.controller.js'; +import { HelloService } from './hello.service.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HelloController], diff --git a/integration/hello-world/src/hello/users/user-by-id.pipe.ts b/integration/hello-world/src/hello/users/user-by-id.pipe.ts index f4405205ffc..31f7e27e895 100644 --- a/integration/hello-world/src/hello/users/user-by-id.pipe.ts +++ b/integration/hello-world/src/hello/users/user-by-id.pipe.ts @@ -1,5 +1,5 @@ import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Injectable() export class UserByIdPipe implements PipeTransform { diff --git a/integration/hello-world/src/host-array/host-array.controller.ts b/integration/hello-world/src/host-array/host-array.controller.ts index 8f6456bef18..6c8a24e3847 100644 --- a/integration/hello-world/src/host-array/host-array.controller.ts +++ b/integration/hello-world/src/host-array/host-array.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Header, HostParam, Param } from '@nestjs/common'; import { Observable, of } from 'rxjs'; -import { HostArrayService } from './host-array.service'; -import { UserByIdPipe } from './users/user-by-id.pipe'; +import { HostArrayService } from './host-array.service.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; @Controller({ path: 'host-array', diff --git a/integration/hello-world/src/host-array/host-array.module.ts b/integration/hello-world/src/host-array/host-array.module.ts index b5f7f77d43d..d13bb3a2336 100644 --- a/integration/hello-world/src/host-array/host-array.module.ts +++ b/integration/hello-world/src/host-array/host-array.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { HostArrayController } from './host-array.controller'; -import { HostArrayService } from './host-array.service'; -import { UsersService } from './users/users.service'; +import { HostArrayController } from './host-array.controller.js'; +import { HostArrayService } from './host-array.service.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HostArrayController], diff --git a/integration/hello-world/src/host-array/users/user-by-id.pipe.ts b/integration/hello-world/src/host-array/users/user-by-id.pipe.ts index f4405205ffc..31f7e27e895 100644 --- a/integration/hello-world/src/host-array/users/user-by-id.pipe.ts +++ b/integration/hello-world/src/host-array/users/user-by-id.pipe.ts @@ -1,5 +1,5 @@ import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Injectable() export class UserByIdPipe implements PipeTransform { diff --git a/integration/hello-world/src/host/host.controller.ts b/integration/hello-world/src/host/host.controller.ts index 8501d59eb87..61f72c474b1 100644 --- a/integration/hello-world/src/host/host.controller.ts +++ b/integration/hello-world/src/host/host.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Header, HostParam, Param } from '@nestjs/common'; import { Observable, of } from 'rxjs'; -import { HostService } from './host.service'; -import { UserByIdPipe } from './users/user-by-id.pipe'; +import { HostService } from './host.service.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; @Controller({ path: 'host', diff --git a/integration/hello-world/src/host/host.module.ts b/integration/hello-world/src/host/host.module.ts index 6d7a733033e..e5c2e49c47c 100644 --- a/integration/hello-world/src/host/host.module.ts +++ b/integration/hello-world/src/host/host.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { HostController } from './host.controller'; -import { HostService } from './host.service'; -import { UsersService } from './users/users.service'; +import { HostController } from './host.controller.js'; +import { HostService } from './host.service.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HostController], diff --git a/integration/hello-world/src/host/users/user-by-id.pipe.ts b/integration/hello-world/src/host/users/user-by-id.pipe.ts index f4405205ffc..31f7e27e895 100644 --- a/integration/hello-world/src/host/users/user-by-id.pipe.ts +++ b/integration/hello-world/src/host/users/user-by-id.pipe.ts @@ -1,5 +1,5 @@ import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Injectable() export class UserByIdPipe implements PipeTransform { diff --git a/integration/hello-world/tsconfig.json b/integration/hello-world/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/hello-world/tsconfig.json +++ b/integration/hello-world/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/hooks/e2e/before-app-shutdown.spec.ts b/integration/hooks/e2e/before-app-shutdown.spec.ts index 152c5efd491..e5c294f8502 100644 --- a/integration/hooks/e2e/before-app-shutdown.spec.ts +++ b/integration/hooks/e2e/before-app-shutdown.spec.ts @@ -1,11 +1,8 @@ import { BeforeApplicationShutdown, Injectable, Module } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as Sinon from 'sinon'; - @Injectable() class TestInjectable implements BeforeApplicationShutdown { - beforeApplicationShutdown = Sinon.spy(); + beforeApplicationShutdown = vi.fn(); } describe('BeforeApplicationShutdown', () => { @@ -17,13 +14,13 @@ describe('BeforeApplicationShutdown', () => { const app = module.createNestApplication(); await app.close(); const instance = module.get(TestInjectable); - expect(instance.beforeApplicationShutdown.called).to.be.true; + expect(instance.beforeApplicationShutdown).toHaveBeenCalled(); }); it('should sort modules by distance (topological sort) - DESC order', async () => { @Injectable() class BB implements BeforeApplicationShutdown { - beforeApplicationShutdown = Sinon.spy(); + beforeApplicationShutdown = vi.fn(); } @Module({ @@ -35,7 +32,7 @@ describe('BeforeApplicationShutdown', () => { @Injectable() class AA implements BeforeApplicationShutdown { constructor(private bb: BB) {} - beforeApplicationShutdown = Sinon.spy(); + beforeApplicationShutdown = vi.fn(); } @Module({ imports: [B], @@ -53,9 +50,54 @@ describe('BeforeApplicationShutdown', () => { const aa = module.get(AA); const bb = module.get(BB); - Sinon.assert.callOrder( - aa.beforeApplicationShutdown, - bb.beforeApplicationShutdown, + expect( + aa.beforeApplicationShutdown.mock.invocationCallOrder[0], + ).toBeLessThan(bb.beforeApplicationShutdown.mock.invocationCallOrder[0]); + }); + + it('should sort components within a single module by injection hierarchy - ASC order', async () => { + @Injectable() + class A implements BeforeApplicationShutdown { + beforeApplicationShutdown = vi.fn(); + } + + @Injectable() + class AHost implements BeforeApplicationShutdown { + constructor(private a: A) {} + beforeApplicationShutdown = vi.fn(); + } + + @Injectable() + class Composition implements BeforeApplicationShutdown { + constructor( + private a: A, + private host: AHost, + ) {} + beforeApplicationShutdown = vi.fn(); + } + + @Module({ + providers: [AHost, A, Composition], + }) + class AModule {} + + const module = await Test.createTestingModule({ + imports: [AModule], + }).compile(); + + const app = module.createNestApplication(); + await app.init(); + await app.close(); + + const child = module.get(A); + const parent = module.get(AHost); + const composition = module.get(Composition); + + expect(composition.beforeApplicationShutdown).toHaveBeenCalledBefore( + parent.beforeApplicationShutdown, + ); + expect(parent.beforeApplicationShutdown).toHaveBeenCalledBefore( + child.beforeApplicationShutdown, ); }); }); diff --git a/integration/hooks/e2e/enable-shutdown-hook.spec.ts b/integration/hooks/e2e/enable-shutdown-hook.spec.ts index 9e35bb1d018..ddec2308ceb 100644 --- a/integration/hooks/e2e/enable-shutdown-hook.spec.ts +++ b/integration/hooks/e2e/enable-shutdown-hook.spec.ts @@ -1,71 +1,81 @@ -import { expect } from 'chai'; import { spawnSync } from 'child_process'; import { join } from 'path'; +const nodeCmd = process.execPath; + +function spawnTsNode(...args: string[]) { + return spawnSync(nodeCmd, ['--import', 'jiti/register', ...args]); +} + describe('enableShutdownHooks', () => { - it('should call the correct hooks if any shutdown signal gets invoked', done => { - const result = spawnSync('ts-node', [ - join(__dirname, '../src/enable-shutdown-hooks-main.ts'), - 'SIGHUP', - ]); - const calls = result.stdout - .toString() - .split('\n') - .map((call: string) => call.trim()); - expect(calls[0]).to.equal('beforeApplicationShutdown SIGHUP'); - expect(calls[1]).to.equal('onApplicationShutdown SIGHUP'); - done(); - }).timeout(10000); + it('should call the correct hooks if any shutdown signal gets invoked', () => + new Promise(done => { + const result = spawnTsNode( + join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'), + 'SIGHUP', + ); + const calls = result.stdout + .toString() + .split('\n') + .map((call: string) => call.trim()); + expect(calls[0]).toBe('beforeApplicationShutdown SIGHUP'); + expect(calls[1]).toBe('onApplicationShutdown SIGHUP'); + done(); + })); - it('should call the correct hooks if a specific shutdown signal gets invoked', done => { - const result = spawnSync('ts-node', [ - join(__dirname, '../src/enable-shutdown-hooks-main.ts'), - 'SIGINT', - 'SIGINT', - ]); - const calls = result.stdout - .toString() - .split('\n') - .map((call: string) => call.trim()); - expect(calls[0]).to.equal('beforeApplicationShutdown SIGINT'); - expect(calls[1]).to.equal('onApplicationShutdown SIGINT'); - done(); - }).timeout(10000); + it('should call the correct hooks if a specific shutdown signal gets invoked', () => + new Promise(done => { + const result = spawnTsNode( + join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'), + 'SIGINT', + 'SIGINT', + ); + const calls = result.stdout + .toString() + .split('\n') + .map((call: string) => call.trim()); + expect(calls[0]).toBe('beforeApplicationShutdown SIGINT'); + expect(calls[1]).toBe('onApplicationShutdown SIGINT'); + done(); + })); - it('should ignore system signals which are not specified', done => { - const result = spawnSync('ts-node', [ - join(__dirname, '../src/enable-shutdown-hooks-main.ts'), - 'SIGINT', - 'SIGHUP', - ]); - expect(result.stdout.toString().trim()).to.be.eq(''); - done(); - }).timeout(10000); + it('should ignore system signals which are not specified', () => + new Promise(done => { + const result = spawnTsNode( + join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'), + 'SIGINT', + 'SIGHUP', + ); + expect(result.stdout.toString().trim()).toBe(''); + done(); + })); - it('should ignore system signals if "enableShutdownHooks" was not called', done => { - const result = spawnSync('ts-node', [ - join(__dirname, '../src/enable-shutdown-hooks-main.ts'), - 'SIGINT', - 'NONE', - ]); - expect(result.stdout.toString().trim()).to.be.eq(''); - done(); - }).timeout(10000); + it('should ignore system signals if "enableShutdownHooks" was not called', () => + new Promise(done => { + const result = spawnTsNode( + join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'), + 'SIGINT', + 'NONE', + ); + expect(result.stdout.toString().trim()).toBe(''); + done(); + })); - it('should call the correct hooks with useProcessExit option', done => { - const result = spawnSync('ts-node', [ - join(__dirname, '../src/enable-shutdown-hooks-main.ts'), - 'SIGHUP', - 'SIGHUP', - 'graceful', - ]); - const calls = result.stdout - .toString() - .split('\n') - .map((call: string) => call.trim()); - expect(calls[0]).to.equal('beforeApplicationShutdown SIGHUP'); - expect(calls[1]).to.equal('onApplicationShutdown SIGHUP'); - expect(result.status).to.equal(0); - done(); - }).timeout(10000); + it('should call the correct hooks with useProcessExit option', () => + new Promise(done => { + const result = spawnTsNode( + join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'), + 'SIGHUP', + 'SIGHUP', + 'graceful', + ); + const calls = result.stdout + .toString() + .split('\n') + .map((call: string) => call.trim()); + expect(calls[0]).toBe('beforeApplicationShutdown SIGHUP'); + expect(calls[1]).toBe('onApplicationShutdown SIGHUP'); + expect(result.status).toBe(0); + done(); + })); }); diff --git a/integration/hooks/e2e/lifecycle-hook-order.spec.ts b/integration/hooks/e2e/lifecycle-hook-order.spec.ts index e5b5a68af10..955e98071f4 100644 --- a/integration/hooks/e2e/lifecycle-hook-order.spec.ts +++ b/integration/hooks/e2e/lifecycle-hook-order.spec.ts @@ -1,13 +1,12 @@ -import { Test } from '@nestjs/testing'; -import * as Sinon from 'sinon'; import { + BeforeApplicationShutdown, Injectable, OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy, OnModuleInit, - BeforeApplicationShutdown, } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; @Injectable() class TestInjectable @@ -18,11 +17,11 @@ class TestInjectable OnApplicationShutdown, BeforeApplicationShutdown { - onApplicationBootstrap = Sinon.spy(); - beforeApplicationShutdown = Sinon.spy(); - onApplicationShutdown = Sinon.spy(); - onModuleDestroy = Sinon.spy(); - onModuleInit = Sinon.spy(); + onApplicationBootstrap = vi.fn(); + beforeApplicationShutdown = vi.fn(); + onApplicationShutdown = vi.fn(); + onModuleDestroy = vi.fn(); + onModuleInit = vi.fn(); } describe('Lifecycle Hook Order', () => { @@ -36,12 +35,17 @@ describe('Lifecycle Hook Order', () => { await app.close(); const instance = module.get(TestInjectable); - Sinon.assert.callOrder( + const order = [ instance.onModuleInit, instance.onApplicationBootstrap, instance.onModuleDestroy, instance.beforeApplicationShutdown, instance.onApplicationShutdown, - ); + ]; + for (let i = 0; i < order.length - 1; i++) { + expect(order[i].mock.invocationCallOrder[0]).toBeLessThan( + order[i + 1].mock.invocationCallOrder[0], + ); + } }); }); diff --git a/integration/hooks/e2e/on-app-boostrap.spec.ts b/integration/hooks/e2e/on-app-boostrap.spec.ts index a5aa6a25355..79acea4c7c3 100644 --- a/integration/hooks/e2e/on-app-boostrap.spec.ts +++ b/integration/hooks/e2e/on-app-boostrap.spec.ts @@ -1,11 +1,8 @@ import { Injectable, Module, OnApplicationBootstrap } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as Sinon from 'sinon'; - @Injectable() class TestInjectable implements OnApplicationBootstrap { - onApplicationBootstrap = Sinon.spy(); + onApplicationBootstrap = vi.fn(); } describe('OnApplicationBootstrap', () => { @@ -17,7 +14,8 @@ describe('OnApplicationBootstrap', () => { const app = module.createNestApplication(); await app.init(); const instance = module.get(TestInjectable); - expect(instance.onApplicationBootstrap.called).to.be.true; + expect(instance.onApplicationBootstrap).toHaveBeenCalled(); + await app.close(); }); it('should not throw an error when onApplicationBootstrap is null', async () => { @@ -28,7 +26,8 @@ describe('OnApplicationBootstrap', () => { }).compile(); const app = module.createNestApplication(); - await app.init().then(obj => expect(obj).to.not.be.undefined); + await app.init().then(obj => expect(obj).not.toBeUndefined()); + await app.close(); }); it('should not throw an error when onApplicationBootstrap is undefined', async () => { @@ -39,7 +38,8 @@ describe('OnApplicationBootstrap', () => { }).compile(); const app = module.createNestApplication(); - await app.init().then(obj => expect(obj).to.not.be.undefined); + await app.init().then(obj => expect(obj).not.toBeUndefined()); + await app.close(); }); it('should sort modules by distance (topological sort) - DESC order', async () => { @@ -80,6 +80,53 @@ describe('OnApplicationBootstrap', () => { await app.init(); const instance = module.get(AA); - expect(instance.field).to.equal('b-field_a-field'); + expect(instance.field).toBe('b-field_a-field'); + await app.close(); + }); + + it('should sort components within a single module by injection hierarchy - DESC order', async () => { + @Injectable() + class A implements OnApplicationBootstrap { + onApplicationBootstrap = vi.fn(); + } + + @Injectable() + class AHost implements OnApplicationBootstrap { + constructor(private a: A) {} + onApplicationBootstrap = vi.fn(); + } + + @Injectable() + class Composition implements OnApplicationBootstrap { + constructor( + private a: A, + private host: AHost, + ) {} + onApplicationBootstrap = vi.fn(); + } + + @Module({ + providers: [AHost, A, Composition], + }) + class AModule {} + + const module = await Test.createTestingModule({ + imports: [AModule], + }).compile(); + + const app = module.createNestApplication(); + await app.init(); + await app.close(); + + const child = module.get(A); + const parent = module.get(AHost); + const composition = module.get(Composition); + + expect(child.onApplicationBootstrap).toHaveBeenCalledBefore( + parent.onApplicationBootstrap, + ); + expect(parent.onApplicationBootstrap).toHaveBeenCalledBefore( + composition.onApplicationBootstrap, + ); }); }); diff --git a/integration/hooks/e2e/on-app-shutdown.spec.ts b/integration/hooks/e2e/on-app-shutdown.spec.ts index 67518878cbd..0b81a81a9a8 100644 --- a/integration/hooks/e2e/on-app-shutdown.spec.ts +++ b/integration/hooks/e2e/on-app-shutdown.spec.ts @@ -1,11 +1,8 @@ import { Injectable, Module, OnApplicationShutdown } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as Sinon from 'sinon'; - @Injectable() class TestInjectable implements OnApplicationShutdown { - onApplicationShutdown = Sinon.spy(); + onApplicationShutdown = vi.fn(); } describe('OnApplicationShutdown', () => { @@ -17,13 +14,13 @@ describe('OnApplicationShutdown', () => { const app = module.createNestApplication(); await app.close(); const instance = module.get(TestInjectable); - expect(instance.onApplicationShutdown.called).to.be.true; + expect(instance.onApplicationShutdown).toHaveBeenCalled(); }); it('should sort modules by distance (topological sort) - DESC order', async () => { @Injectable() class BB implements OnApplicationShutdown { - onApplicationShutdown = Sinon.spy(); + onApplicationShutdown = vi.fn(); } @Module({ @@ -35,7 +32,7 @@ describe('OnApplicationShutdown', () => { @Injectable() class AA implements OnApplicationShutdown { constructor(private bb: BB) {} - onApplicationShutdown = Sinon.spy(); + onApplicationShutdown = vi.fn(); } @Module({ imports: [B], @@ -53,6 +50,54 @@ describe('OnApplicationShutdown', () => { const aa = module.get(AA); const bb = module.get(BB); - Sinon.assert.callOrder(aa.onApplicationShutdown, bb.onApplicationShutdown); + expect(aa.onApplicationShutdown.mock.invocationCallOrder[0]).toBeLessThan( + bb.onApplicationShutdown.mock.invocationCallOrder[0], + ); + }); + + it('should sort components within a single module by injection hierarchy - ASC order', async () => { + @Injectable() + class A implements OnApplicationShutdown { + onApplicationShutdown = vi.fn(); + } + + @Injectable() + class AHost implements OnApplicationShutdown { + constructor(private a: A) {} + onApplicationShutdown = vi.fn(); + } + + @Injectable() + class Composition implements OnApplicationShutdown { + constructor( + private a: A, + private host: AHost, + ) {} + onApplicationShutdown = vi.fn(); + } + + @Module({ + providers: [AHost, A, Composition], + }) + class AModule {} + + const module = await Test.createTestingModule({ + imports: [AModule], + }).compile(); + + const app = module.createNestApplication(); + await app.init(); + await app.close(); + + const child = module.get(A); + const parent = module.get(AHost); + const composition = module.get(Composition); + + expect(composition.onApplicationShutdown).toHaveBeenCalledBefore( + parent.onApplicationShutdown, + ); + expect(parent.onApplicationShutdown).toHaveBeenCalledBefore( + child.onApplicationShutdown, + ); }); }); diff --git a/integration/hooks/e2e/on-module-destroy.spec.ts b/integration/hooks/e2e/on-module-destroy.spec.ts index 52468cebea6..2d855d821e0 100644 --- a/integration/hooks/e2e/on-module-destroy.spec.ts +++ b/integration/hooks/e2e/on-module-destroy.spec.ts @@ -1,11 +1,8 @@ import { Injectable, Module, OnModuleDestroy } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as Sinon from 'sinon'; - @Injectable() class TestInjectable implements OnModuleDestroy { - onModuleDestroy = Sinon.spy(); + onModuleDestroy = vi.fn(); } describe('OnModuleDestroy', () => { @@ -17,7 +14,7 @@ describe('OnModuleDestroy', () => { const app = module.createNestApplication(); await app.close(); const instance = module.get(TestInjectable); - expect(instance.onModuleDestroy.called).to.be.true; + expect(instance.onModuleDestroy).toHaveBeenCalled(); }); it('should not throw an error when onModuleDestroy is null', async () => { @@ -26,7 +23,8 @@ describe('OnModuleDestroy', () => { }).compile(); const app = module.createNestApplication(); - await app.init().then(obj => expect(obj).to.not.be.undefined); + await app.init().then(obj => expect(obj).not.toBeUndefined()); + await app.close(); }); it('should not throw an error when onModuleDestroy is undefined', async () => { @@ -37,13 +35,14 @@ describe('OnModuleDestroy', () => { }).compile(); const app = module.createNestApplication(); - await app.init().then(obj => expect(obj).to.not.be.undefined); + await app.init().then(obj => expect(obj).not.toBeUndefined()); + await app.close(); }); it('should sort modules by distance (topological sort) - DESC order', async () => { @Injectable() class BB implements OnModuleDestroy { - onModuleDestroy = Sinon.spy(); + onModuleDestroy = vi.fn(); } @Module({ @@ -55,7 +54,7 @@ describe('OnModuleDestroy', () => { @Injectable() class AA implements OnModuleDestroy { constructor(private bb: BB) {} - onModuleDestroy = Sinon.spy(); + onModuleDestroy = vi.fn(); } @Module({ @@ -74,6 +73,53 @@ describe('OnModuleDestroy', () => { const aa = module.get(AA); const bb = module.get(BB); - Sinon.assert.callOrder(aa.onModuleDestroy, bb.onModuleDestroy); + expect(aa.onModuleDestroy.mock.invocationCallOrder[0]).toBeLessThan( + bb.onModuleDestroy.mock.invocationCallOrder[0], + ); + }); + + it('should sort components within a single module by injection hierarchy - ASC order', async () => { + @Injectable() + class A implements OnModuleDestroy { + onModuleDestroy = vi.fn(); + } + + @Injectable() + class AHost implements OnModuleDestroy { + constructor(private a: A) {} + onModuleDestroy = vi.fn(); + } + + @Injectable() + class Composition implements OnModuleDestroy { + constructor( + private a: A, + private host: AHost, + ) {} + onModuleDestroy = vi.fn(); + } + + @Module({ + providers: [AHost, A, Composition], + }) + class AModule {} + + const module = await Test.createTestingModule({ + imports: [AModule], + }).compile(); + + const app = module.createNestApplication(); + await app.init(); + await app.close(); + + const child = module.get(A); + const parent = module.get(AHost); + const composition = module.get(Composition); + expect(composition.onModuleDestroy).toHaveBeenCalledBefore( + parent.onModuleDestroy, + ); + expect(parent.onModuleDestroy).toHaveBeenCalledBefore( + child.onModuleDestroy, + ); }); }); diff --git a/integration/hooks/e2e/on-module-init.spec.ts b/integration/hooks/e2e/on-module-init.spec.ts index d35a22d6987..69dcd925373 100644 --- a/integration/hooks/e2e/on-module-init.spec.ts +++ b/integration/hooks/e2e/on-module-init.spec.ts @@ -1,11 +1,8 @@ import { Injectable, Module, OnModuleInit } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as Sinon from 'sinon'; - @Injectable() class TestInjectable implements OnModuleInit { - onModuleInit = Sinon.spy(); + onModuleInit = vi.fn(); } describe('OnModuleInit', () => { @@ -17,7 +14,8 @@ describe('OnModuleInit', () => { const app = module.createNestApplication(); await app.init(); const instance = module.get(TestInjectable); - expect(instance.onModuleInit.called).to.be.true; + expect(instance.onModuleInit).toHaveBeenCalled(); + await app.close(); }); it('should not throw an error when onModuleInit is null', async () => { @@ -26,7 +24,8 @@ describe('OnModuleInit', () => { }).compile(); const app = module.createNestApplication(); - await app.init().then(obj => expect(obj).to.not.be.undefined); + await app.init().then(obj => expect(obj).not.toBeUndefined()); + await app.close(); }); it('should not throw an error when onModuleInit is undefined', async () => { @@ -35,7 +34,8 @@ describe('OnModuleInit', () => { }).compile(); const app = module.createNestApplication(); - await app.init().then(obj => expect(obj).to.not.be.undefined); + await app.init().then(obj => expect(obj).not.toBeUndefined()); + await app.close(); }); it('should sort modules by distance (topological sort) - DESC order', async () => { @@ -109,6 +109,52 @@ describe('OnModuleInit', () => { await app.init(); const instance = module.get(AA); - expect(instance.field).to.equal('c-field_b-field_a-field'); + expect(instance.field).toBe('c-field_b-field_a-field'); + await app.close(); + }); + + it('should sort components within a single module by injection hierarchy - DESC order', async () => { + @Injectable() + class A implements OnModuleInit { + onModuleInit = vi.fn(); + } + + @Injectable() + class AHost implements OnModuleInit { + constructor(private a: A) {} + onModuleInit = vi.fn(); + } + + @Injectable() + class Composition implements OnModuleInit { + constructor( + private a: A, + private host: AHost, + ) {} + onModuleInit = vi.fn(); + } + + @Module({ + providers: [AHost, A, Composition], + }) + class AModule {} + + const module = await Test.createTestingModule({ + imports: [AModule], + }).compile(); + + const app = module.createNestApplication(); + await app.init(); + await app.close(); + + const child = module.get(A); + const parent = module.get(AHost); + const composition = module.get(Composition); + expect(child.onModuleInit.mock.invocationCallOrder[0]).toBeLessThan( + parent.onModuleInit.mock.invocationCallOrder[0], + ); + expect(parent.onModuleInit.mock.invocationCallOrder[0]).toBeLessThan( + composition.onModuleInit.mock.invocationCallOrder[0], + ); }); }); diff --git a/integration/hooks/tsconfig.json b/integration/hooks/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/hooks/tsconfig.json +++ b/integration/hooks/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/injector/e2e/circular-custom-providers.spec.ts b/integration/injector/e2e/circular-custom-providers.spec.ts index b05a95ea365..6e31227867e 100644 --- a/integration/injector/e2e/circular-custom-providers.spec.ts +++ b/integration/injector/e2e/circular-custom-providers.spec.ts @@ -1,5 +1,4 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { Controller, Injectable, Module } from '@nestjs/common'; @@ -29,35 +28,23 @@ export class AppModule {} describe('Circular custom providers', () => { it('should throw an exception (useClass + regular provider)', async () => { - try { - const builder = Test.createTestingModule({ - imports: [AppModule], - }); - await builder.compile(); - - expect(true).to.be.eql(false); - } catch (err) { - expect(err.message).to.be.eql( - 'A circular dependency has been detected inside "A". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.', - ); - } + const builder = Test.createTestingModule({ + imports: [AppModule], + }); + await expect(builder.compile()).rejects.toThrow( + 'A circular dependency has been detected inside "A". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.', + ); }); it('should throw an exception (2 factories)', async () => { - try { - const builder = Test.createTestingModule({ - providers: [ - { provide: 'ABC', useFactory: () => ({}), inject: ['DEF'] }, - { provide: 'DEF', useFactory: () => ({}), inject: ['ABC'] }, - ], - }); - await builder.compile(); - - expect(true).to.be.eql(false); - } catch (err) { - expect(err.message).to.be.eql( - 'A circular dependency has been detected inside "ABC". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.', - ); - } + const builder = Test.createTestingModule({ + providers: [ + { provide: 'ABC', useFactory: () => ({}), inject: ['DEF'] }, + { provide: 'DEF', useFactory: () => ({}), inject: ['ABC'] }, + ], + }); + await expect(builder.compile()).rejects.toThrow( + 'A circular dependency has been detected inside "ABC"', + ); }); }); diff --git a/integration/injector/e2e/circular-modules.spec.ts b/integration/injector/e2e/circular-modules.spec.ts index 653a0b98a9d..1f8c0f86625 100644 --- a/integration/injector/e2e/circular-modules.spec.ts +++ b/integration/injector/e2e/circular-modules.spec.ts @@ -1,9 +1,8 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { CircularModule } from '../src/circular-modules/circular.module'; -import { CircularService } from '../src/circular-modules/circular.service'; -import { InputModule } from '../src/circular-modules/input.module'; -import { InputService } from '../src/circular-modules/input.service'; +import { CircularModule } from '../src/circular-modules/circular.module.js'; +import { CircularService } from '../src/circular-modules/circular.service.js'; +import { InputModule } from '../src/circular-modules/input.module.js'; +import { InputService } from '../src/circular-modules/input.service.js'; describe('Circular dependency (modules)', () => { it('should resolve circular dependency between providers', async () => { @@ -14,7 +13,7 @@ describe('Circular dependency (modules)', () => { const inputService = testingModule.get(InputService); const circularService = testingModule.get(CircularService); - expect(inputService.service).to.be.eql(circularService); - expect(circularService.service).to.be.eql(inputService); + expect(inputService.service).toEqual(circularService); + expect(circularService.service).toEqual(inputService); }); }); diff --git a/integration/injector/e2e/circular-property-injection.spec.ts b/integration/injector/e2e/circular-property-injection.spec.ts index 23997c941d0..f9ec6884c1a 100644 --- a/integration/injector/e2e/circular-property-injection.spec.ts +++ b/integration/injector/e2e/circular-property-injection.spec.ts @@ -1,9 +1,8 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { CircularPropertiesModule } from '../src/circular-properties/circular-properties.module'; -import { CircularService } from '../src/circular-properties/circular.service'; -import { InputPropertiesModule } from '../src/circular-properties/input-properties.module'; -import { InputService } from '../src/circular-properties/input.service'; +import { CircularPropertiesModule } from '../src/circular-properties/circular-properties.module.js'; +import { CircularService } from '../src/circular-properties/circular.service.js'; +import { InputPropertiesModule } from '../src/circular-properties/input-properties.module.js'; +import { InputService } from '../src/circular-properties/input.service.js'; describe('Circular properties dependency (modules)', () => { it('should resolve circular dependency between providers', async () => { @@ -14,7 +13,7 @@ describe('Circular properties dependency (modules)', () => { const inputService = testingModule.get(InputService); const circularService = testingModule.get(CircularService); - expect(inputService.service).to.be.eql(circularService); - expect(circularService.service).to.be.eql(inputService); + expect(inputService.service).toEqual(circularService); + expect(circularService.service).toEqual(inputService); }); }); diff --git a/integration/injector/e2e/circular-structure-dynamic-modules.spec.ts b/integration/injector/e2e/circular-structure-dynamic-modules.spec.ts index 5042fed2c27..c5300f8ff81 100644 --- a/integration/injector/e2e/circular-structure-dynamic-modules.spec.ts +++ b/integration/injector/e2e/circular-structure-dynamic-modules.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; import { Test } from '@nestjs/testing'; -import { CircularModule } from '../src/circular-structure-dynamic-module/circular.module'; -import { InputService } from '../src/circular-structure-dynamic-module/input.service'; +import { CircularModule } from '../src/circular-structure-dynamic-module/circular.module.js'; +import { InputService } from '../src/circular-structure-dynamic-module/input.service.js'; describe('Circular structure for dynamic modules', () => { it('should resolve circular structure with dynamic modules', async () => { @@ -11,6 +10,6 @@ describe('Circular structure for dynamic modules', () => { const testingModule = await builder.compile(); const inputService = testingModule.get(InputService); - expect(inputService).to.be.instanceof(InputService); + expect(inputService).toBeInstanceOf(InputService); }); }); diff --git a/integration/injector/e2e/circular.spec.ts b/integration/injector/e2e/circular.spec.ts index 2ca646cc895..258b5d68da5 100644 --- a/integration/injector/e2e/circular.spec.ts +++ b/integration/injector/e2e/circular.spec.ts @@ -1,8 +1,7 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { CircularModule } from '../src/circular/circular.module'; -import { CircularService } from '../src/circular/circular.service'; -import { InputService } from '../src/circular/input.service'; +import { CircularModule } from '../src/circular/circular.module.js'; +import { CircularService } from '../src/circular/circular.service.js'; +import { InputService } from '../src/circular/input.service.js'; describe('Circular dependency', () => { it('should resolve circular dependency between providers', async () => { @@ -13,7 +12,7 @@ describe('Circular dependency', () => { const inputService = testingModule.get(InputService); const circularService = testingModule.get(CircularService); - expect(inputService.service).to.be.eql(circularService); - expect(circularService.service).to.be.eql(inputService); + expect(inputService.service).toEqual(circularService); + expect(circularService.service).toEqual(inputService); }); }); diff --git a/integration/injector/e2e/core-injectables.spec.ts b/integration/injector/e2e/core-injectables.spec.ts index ab0b10e59e8..33e7a3eb0b2 100644 --- a/integration/injector/e2e/core-injectables.spec.ts +++ b/integration/injector/e2e/core-injectables.spec.ts @@ -1,7 +1,6 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import { CoreInjectablesModule } from '../src/core-injectables/core-injectables.module'; import { ApplicationConfig, ModuleRef } from '@nestjs/core'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CoreInjectablesModule } from '../src/core-injectables/core-injectables.module.js'; describe('Core Injectables', () => { let testingModule: TestingModule; @@ -13,26 +12,30 @@ describe('Core Injectables', () => { testingModule = await builder.compile(); }); + afterEach(async () => { + await testingModule.close(); + }); + it('should provide ApplicationConfig as core injectable', () => { const applicationConfig = testingModule.get(ApplicationConfig); applicationConfig.setGlobalPrefix('/api'); - expect(applicationConfig).to.not.be.undefined; - expect(applicationConfig.getGlobalPrefix()).to.be.eq('/api'); + expect(applicationConfig).not.toBeUndefined(); + expect(applicationConfig.getGlobalPrefix()).toBe('/api'); }); it('should provide ModuleRef as core injectable', () => { const moduleRef = testingModule.get(ModuleRef); - expect(moduleRef).to.not.be.undefined; + expect(moduleRef).not.toBeUndefined(); }); it('should provide the current Module as provider', () => { const module = testingModule.get( CoreInjectablesModule, ); - expect(module).to.not.be.undefined; - expect(module.constructor.name).to.be.eq('CoreInjectablesModule'); + expect(module).not.toBeUndefined(); + expect(module.constructor.name).toBe('CoreInjectablesModule'); }); }); diff --git a/integration/injector/e2e/default-values.spec.ts b/integration/injector/e2e/default-values.spec.ts index fc8da7db35a..17b1ee95ee3 100644 --- a/integration/injector/e2e/default-values.spec.ts +++ b/integration/injector/e2e/default-values.spec.ts @@ -1,7 +1,6 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { DefaultsModule } from '../src/defaults/defaults.module'; -import { DefaultsService } from '../src/defaults/defaults.service'; +import { DefaultsModule } from '../src/defaults/defaults.module.js'; +import { DefaultsService } from '../src/defaults/defaults.service.js'; describe('Injector', () => { describe('when optional', () => { @@ -10,7 +9,7 @@ describe('Injector', () => { imports: [DefaultsModule], }); const app = await builder.compile(); - expect(app.get(DefaultsService).coreService.default).to.be.true; + expect(app.get(DefaultsService).coreService.default).toBe(true); }); }); }); diff --git a/integration/injector/e2e/inherited-optional.spec.ts b/integration/injector/e2e/inherited-optional.spec.ts new file mode 100644 index 00000000000..fe6ab8a47ae --- /dev/null +++ b/integration/injector/e2e/inherited-optional.spec.ts @@ -0,0 +1,58 @@ +import { Injectable, mixin, Module, Optional } from '@nestjs/common'; +import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception.js'; +import { Test } from '@nestjs/testing'; + +@Injectable() +class NeededService { + exec() { + return 'exec'; + } +} + +@Module({ + providers: [NeededService], + exports: [NeededService], +}) +class NeededModule {} + +const Foo = () => { + class FooMixin { + constructor(@Optional() option: any) {} + } + return mixin(FooMixin); +}; + +@Injectable() +class FooService extends Foo() { + constructor(private readonly neededService: NeededService) { + super(); + } + + doSomething() { + return this.neededService.exec(); + } +} + +@Module({ + imports: [], + providers: [FooService], + exports: [FooService], +}) +class FooModule {} + +describe('Inherited optional dependency', () => { + /** + * You can see details on this issue here: https://github.com/nestjs/nest/issues/2581 + */ + describe('when the parent has an @Optional() parameter', () => { + it('should throw an UnknownDependenciesException due to the missing dependency', async () => { + const module = Test.createTestingModule({ + imports: [NeededModule, FooModule], + }); + + await expect(module.compile()).rejects.toBeInstanceOf( + UnknownDependenciesException, + ); + }); + }); +}); diff --git a/integration/injector/e2e/injector.spec.ts b/integration/injector/e2e/injector.spec.ts index 5fd489676f1..89388aa1c54 100644 --- a/integration/injector/e2e/injector.spec.ts +++ b/integration/injector/e2e/injector.spec.ts @@ -1,36 +1,30 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; -import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception'; -import { UnknownExportException } from '@nestjs/core/errors/exceptions/unknown-export.exception'; +import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception.js'; +import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception.js'; +import { UnknownExportException } from '@nestjs/core/errors/exceptions/unknown-export.exception.js'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as chai from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; import { DYNAMIC_TOKEN, DYNAMIC_VALUE, NestDynamicModule, -} from '../src/dynamic/dynamic.module'; -import { ExportsModule } from '../src/exports/exports.module'; -import { InjectModule } from '../src/inject/inject.module'; -import { InjectSameNameModule } from '../src/inject/inject-same-name.module'; +} from '../src/dynamic/dynamic.module.js'; +import { ExportsModule } from '../src/exports/exports.module.js'; +import { InjectSameNameModule } from '../src/inject/inject-same-name.module.js'; +import { InjectModule } from '../src/inject/inject.module.js'; import { - SelfInjectionProviderModule, - SelfInjectionProviderCustomTokenModule, SelfInjectionForwardProviderModule, -} from '../src/self-injection/self-injection-provider.module'; -chai.use(chaiAsPromised); + SelfInjectionProviderCustomTokenModule, + SelfInjectionProviderModule, +} from '../src/self-injection/self-injection-provider.module.js'; describe('Injector', () => { describe('when "providers" and "exports" properties are inconsistent', () => { it(`should fail with "UnknownExportException"`, async () => { - try { - const builder = Test.createTestingModule({ - imports: [ExportsModule], - }); - await builder.compile(); - } catch (err) { - expect(err).to.be.instanceof(UnknownExportException); - } + const builder = Test.createTestingModule({ + imports: [ExportsModule], + }); + await expect(builder.compile()).rejects.toBeInstanceOf( + UnknownExportException, + ); }); }); @@ -40,20 +34,16 @@ describe('Injector', () => { imports: [InjectSameNameModule], }); - await expect(builder.compile()).to.eventually.be.fulfilled; + await expect(builder.compile()).resolves.toBeDefined(); }); }); describe('when Nest cannot resolve dependencies', () => { it(`should fail with "RuntimeException"`, async () => { - try { - const builder = Test.createTestingModule({ - imports: [InjectModule], - }); - await builder.compile(); - } catch (err) { - expect(err).to.be.instanceof(RuntimeException); - } + const builder = Test.createTestingModule({ + imports: [InjectModule], + }); + await expect(builder.compile()).rejects.toBeInstanceOf(RuntimeException); }); describe('due to self-injection providers', () => { @@ -62,9 +52,7 @@ describe('Injector', () => { imports: [SelfInjectionProviderModule], }); - await expect( - builder.compile(), - ).to.eventually.be.rejected.and.be.an.instanceOf( + await expect(builder.compile()).rejects.toBeInstanceOf( UnknownDependenciesException, ); }); @@ -73,9 +61,7 @@ describe('Injector', () => { imports: [SelfInjectionForwardProviderModule], }); - await expect( - builder.compile(), - ).to.eventually.be.rejected.and.be.an.instanceOf( + await expect(builder.compile()).rejects.toBeInstanceOf( UnknownDependenciesException, ); }); @@ -84,9 +70,7 @@ describe('Injector', () => { imports: [SelfInjectionProviderCustomTokenModule], }); - await expect( - builder.compile(), - ).to.eventually.be.rejected.and.be.an.instanceOf( + await expect(builder.compile()).rejects.toBeInstanceOf( UnknownDependenciesException, ); }); @@ -99,7 +83,7 @@ describe('Injector', () => { imports: [NestDynamicModule.byObject()], }); const app = await builder.compile(); - expect(app.get(DYNAMIC_TOKEN)).to.be.eql(DYNAMIC_VALUE); + expect(app.get(DYNAMIC_TOKEN)).toEqual(DYNAMIC_VALUE); }); it(`should return provider via token (exported by token)`, async () => { @@ -107,7 +91,7 @@ describe('Injector', () => { imports: [NestDynamicModule.byName()], }); const app = await builder.compile(); - expect(app.get(DYNAMIC_TOKEN)).to.be.eql(DYNAMIC_VALUE); + expect(app.get(DYNAMIC_TOKEN)).toEqual(DYNAMIC_VALUE); }); }); }); diff --git a/integration/injector/e2e/introspection.spec.ts b/integration/injector/e2e/introspection.spec.ts index 6a24c6ddc7a..21265f6ea00 100644 --- a/integration/injector/e2e/introspection.spec.ts +++ b/integration/injector/e2e/introspection.spec.ts @@ -1,10 +1,9 @@ import { Scope } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import { ScopedModule, STATIC_FACTORY } from '../src/scoped/scoped.module'; -import { ScopedService } from '../src/scoped/scoped.service'; -import { TransientService } from '../src/scoped/transient.service'; +import { ScopedModule, STATIC_FACTORY } from '../src/scoped/scoped.module.js'; +import { ScopedService } from '../src/scoped/scoped.service.js'; +import { TransientService } from '../src/scoped/transient.service.js'; describe('Providers introspection', () => { let testingModule: TestingModule; @@ -17,18 +16,22 @@ describe('Providers introspection', () => { moduleRef = testingModule.get(ModuleRef); }); + afterEach(async () => { + await testingModule.close(); + }); + it('should properly introspect a transient provider', async () => { const introspectionResult = moduleRef.introspect(TransientService); - expect(introspectionResult.scope).to.be.equal(Scope.TRANSIENT); + expect(introspectionResult.scope).toBe(Scope.TRANSIENT); }); it('should properly introspect a singleton provider', async () => { const introspectionResult = moduleRef.introspect(STATIC_FACTORY); - expect(introspectionResult.scope).to.be.equal(Scope.DEFAULT); + expect(introspectionResult.scope).toBe(Scope.DEFAULT); }); it('should properly introspect a request-scoped provider', async () => { const introspectionResult = moduleRef.introspect(ScopedService); - expect(introspectionResult.scope).to.be.equal(Scope.REQUEST); + expect(introspectionResult.scope).toBe(Scope.REQUEST); }); }); diff --git a/integration/injector/e2e/many-global-modules.spec.ts b/integration/injector/e2e/many-global-modules.spec.ts index 60118ca6e63..e4c70ba1030 100644 --- a/integration/injector/e2e/many-global-modules.spec.ts +++ b/integration/injector/e2e/many-global-modules.spec.ts @@ -1,6 +1,4 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; import { Global, Inject, Injectable, Module, Scope } from '@nestjs/common'; @Global() @@ -64,11 +62,11 @@ export class Dependant { ) {} public checkDependencies() { - expect(this.transientProvider).to.be.instanceOf(TransientProvider); - expect(this.foreignTransientProvider).to.be.instanceOf( + expect(this.transientProvider).toBeInstanceOf(TransientProvider); + expect(this.foreignTransientProvider).toBeInstanceOf( ForeignTransientProvider, ); - expect(this.requestProvider).to.be.instanceOf(RequestProvider); + expect(this.requestProvider).toBeInstanceOf(RequestProvider); } } @@ -149,9 +147,9 @@ describe('Many global modules', () => { const moduleRef = await moduleBuilder.compile(); const dependant = await moduleRef.resolve(Dependant); - const checkDependenciesSpy = sinon.spy(dependant, 'checkDependencies'); + const checkDependenciesSpy = vi.spyOn(dependant, 'checkDependencies'); dependant.checkDependencies(); - expect(checkDependenciesSpy.called).to.be.true; + expect(checkDependenciesSpy).toHaveBeenCalled(); }); }); diff --git a/integration/injector/e2e/multiple-providers.spec.ts b/integration/injector/e2e/multiple-providers.spec.ts index 0f5c906fdc8..c5782cd595a 100644 --- a/integration/injector/e2e/multiple-providers.spec.ts +++ b/integration/injector/e2e/multiple-providers.spec.ts @@ -1,6 +1,5 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { MultipleProvidersModule } from '../src/multiple-providers/multiple-providers.module'; +import { MultipleProvidersModule } from '../src/multiple-providers/multiple-providers.module.js'; describe('Multiple providers under the same token ("each" feature)', () => { describe('get()', () => { @@ -20,7 +19,7 @@ describe('Multiple providers under the same token ("each" feature)', () => { // @ts-expect-error: make sure "multiProviderInstances" is string[] not string multiProviderInstances.charAt; - expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']); + expect(multiProviderInstances).toEqual(['A', 'B', 'C']); }); }); describe('resolve()', () => { @@ -41,7 +40,7 @@ describe('Multiple providers under the same token ("each" feature)', () => { // @ts-expect-error: make sure "multiProviderInstances" is string[] not string multiProviderInstances.charAt; - expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']); + expect(multiProviderInstances).toEqual(['A', 'B', 'C']); }); it('should return an array of default-scoped providers', async () => { @@ -61,7 +60,7 @@ describe('Multiple providers under the same token ("each" feature)', () => { // @ts-expect-error: make sure "multiProviderInstances" is string[] not string multiProviderInstances.charAt; - expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']); + expect(multiProviderInstances).toEqual(['A', 'B', 'C']); }); }); }); diff --git a/integration/injector/e2e/optional-factory-provider-dep.spec.ts b/integration/injector/e2e/optional-factory-provider-dep.spec.ts index 22a4113cd42..1dff3f18336 100644 --- a/integration/injector/e2e/optional-factory-provider-dep.spec.ts +++ b/integration/injector/e2e/optional-factory-provider-dep.spec.ts @@ -1,7 +1,6 @@ import { Scope } from '@nestjs/common'; -import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception'; +import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception.js'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; describe('Optional factory provider deps', () => { describe('when dependency is optional', () => { @@ -20,7 +19,7 @@ describe('Optional factory provider deps', () => { }).compile(); const factoryProvider = moduleRef.get('FACTORY'); - expect(factoryProvider).to.equal('OPTIONAL_DEP_VALUE'); + expect(factoryProvider).toBe('OPTIONAL_DEP_VALUE'); }); }); describe('otherwise', () => { @@ -37,7 +36,7 @@ describe('Optional factory provider deps', () => { }).compile(); const factoryProvider = moduleRef.get('FACTORY'); - expect(factoryProvider).to.equal(defaultValue); + expect(factoryProvider).toBe(defaultValue); }); it('"undefined" should be injected into the factory function (scoped provider)', async () => { const MY_PROVIDER = 'MY_PROVIDER'; @@ -70,7 +69,7 @@ describe('Optional factory provider deps', () => { ], }).compile(); - expect(await module.resolve(MY_PROVIDER)).to.deep.equal({ + expect(await module.resolve(MY_PROVIDER)).toEqual({ first: undefined, second: 'second', }); @@ -80,56 +79,44 @@ describe('Optional factory provider deps', () => { describe('otherwise', () => { describe('and dependency is not registered', () => { it('should error out', async () => { - try { - const builder = Test.createTestingModule({ - providers: [ - { - provide: 'FACTORY', - useFactory: () => 'RETURNED_VALUE', - inject: ['MISSING_DEP'], - }, - ], - }); - await builder.compile(); - } catch (err) { - expect(err).to.be.instanceOf(UnknownDependenciesException); - } - }); - }); - }); - describe('and dependency is registered but it cannot be instantiated', () => { - it('should error out', async () => { - try { const builder = Test.createTestingModule({ providers: [ - { - provide: 'POSSIBLY_MISSING_DEP', - useFactory: () => null, - inject: ['MISSING_DEP'], - }, { provide: 'FACTORY', useFactory: () => 'RETURNED_VALUE', - inject: [{ token: 'POSSIBLY_MISSING_DEP', optional: false }], + inject: ['MISSING_DEP'], }, ], }); - await builder.compile(); - } catch (err) { - expect(err).to.be.instanceOf(UnknownDependenciesException); - expect(err.message).to - .equal(`Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?). Please make sure that the argument "MISSING_DEP" at index [0] is available in the RootTestModule context. - -Potential solutions: -- Is RootTestModule a valid NestJS module? -- If "MISSING_DEP" is a provider, is it part of the current RootTestModule? -- If "MISSING_DEP" is exported from a separate @Module, is that module imported within RootTestModule? - @Module({ - imports: [ /* the Module containing "MISSING_DEP" */ ] - }) - -For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`); - } + await expect(builder.compile()).rejects.toBeInstanceOf( + UnknownDependenciesException, + ); + }); + }); + }); + describe('and dependency is registered but it cannot be instantiated', () => { + it('should error out', async () => { + const builder = Test.createTestingModule({ + providers: [ + { + provide: 'POSSIBLY_MISSING_DEP', + useFactory: () => null, + inject: ['MISSING_DEP'], + }, + { + provide: 'FACTORY', + useFactory: () => 'RETURNED_VALUE', + inject: [{ token: 'POSSIBLY_MISSING_DEP', optional: false }], + }, + ], + }); + await expect(builder.compile()).rejects.toSatisfy((err: any) => { + expect(err).toBeInstanceOf(UnknownDependenciesException); + expect(err.message).toContain( + `Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?)`, + ); + return true; + }); }); }); }); diff --git a/integration/injector/e2e/property-injection.spec.ts b/integration/injector/e2e/property-injection.spec.ts index 7d4582f4ce1..edf00669c10 100644 --- a/integration/injector/e2e/property-injection.spec.ts +++ b/integration/injector/e2e/property-injection.spec.ts @@ -1,9 +1,8 @@ +import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception.js'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { DependencyService } from '../src/properties/dependency.service'; -import { PropertiesModule } from '../src/properties/properties.module'; -import { PropertiesService } from '../src/properties/properties.service'; -import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception'; +import { DependencyService } from '../src/properties/dependency.service.js'; +import { PropertiesModule } from '../src/properties/properties.module.js'; +import { PropertiesService } from '../src/properties/properties.service.js'; describe('Injector', () => { it('should resolve property-based dependencies', async () => { @@ -13,32 +12,25 @@ describe('Injector', () => { const app = await builder.compile(); const dependency = app.get(DependencyService); - expect(app.get(PropertiesService).service).to.be.eql(dependency); - expect(app.get(PropertiesService).token).to.be.true; - expect(app.get(PropertiesService).symbolToken).to.be.true; + expect(app.get(PropertiesService).service).toEqual(dependency); + expect(app.get(PropertiesService).token).toBe(true); + expect(app.get(PropertiesService).symbolToken).toBe(true); }); it('should throw UnknownDependenciesException when dependency is not met', async () => { - let exception; - - try { - const builder = Test.createTestingModule({ - providers: [ - DependencyService, - PropertiesService, - { - provide: 'token', - useValue: true, - }, - // symbol token is missing here - ], - }); - const app = await builder.compile(); - app.get(DependencyService); - } catch (e) { - exception = e; - } - - expect(exception).to.be.instanceOf(UnknownDependenciesException); + const builder = Test.createTestingModule({ + providers: [ + DependencyService, + PropertiesService, + { + provide: 'token', + useValue: true, + }, + // symbol token is missing here + ], + }); + await expect(builder.compile()).rejects.toBeInstanceOf( + UnknownDependenciesException, + ); }); }); diff --git a/integration/injector/e2e/request-scope-bubbling.spec.ts b/integration/injector/e2e/request-scope-bubbling.spec.ts index ae0378d9886..55f4b5ddc54 100644 --- a/integration/injector/e2e/request-scope-bubbling.spec.ts +++ b/integration/injector/e2e/request-scope-bubbling.spec.ts @@ -1,7 +1,6 @@ import { Injectable, Scope, Module } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ContextIdFactory, REQUEST } from '@nestjs/core'; -import { expect } from 'chai'; describe('Request Scope Bubbling', () => { // 1. Define the "Poison" (Request Scoped Service) @@ -39,10 +38,10 @@ describe('Request Scope Bubbling', () => { // 6. Assertions (The "Moment of Truth") // The Child IDs should be different (Proof of Request Scope) - expect(parent1.child.id).to.not.equal(parent2.child.id); + expect(parent1.child.id).not.toBe(parent2.child.id); // The Parent instances should ALSO be different (Proof of Bubbling) // If Parent was a true Singleton, these would be equal. - expect(parent1).to.not.equal(parent2); + expect(parent1).not.toBe(parent2); }); }); diff --git a/integration/injector/e2e/scoped-instances.spec.ts b/integration/injector/e2e/scoped-instances.spec.ts index 04cab4e5ffd..55a6154a368 100644 --- a/integration/injector/e2e/scoped-instances.spec.ts +++ b/integration/injector/e2e/scoped-instances.spec.ts @@ -1,17 +1,16 @@ import { createContextId } from '@nestjs/core'; -import { InvalidClassScopeException } from '@nestjs/core/errors/exceptions/invalid-class-scope.exception'; +import { InvalidClassScopeException } from '@nestjs/core/errors/exceptions/invalid-class-scope.exception.js'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import { ScopedController } from '../src/scoped/scoped.controller'; +import { ScopedController } from '../src/scoped/scoped.controller.js'; import { REQUEST_SCOPED_FACTORY, ScopedModule, STATIC_FACTORY, TRANSIENT_SCOPED_FACTORY, -} from '../src/scoped/scoped.module'; -import { ScopedService } from '../src/scoped/scoped.service'; -import { TransientService } from '../src/scoped/transient.service'; -import { Transient3Service } from '../src/scoped/transient3.service'; +} from '../src/scoped/scoped.module.js'; +import { ScopedService } from '../src/scoped/scoped.service.js'; +import { TransientService } from '../src/scoped/transient.service.js'; +import { Transient3Service } from '../src/scoped/transient3.service.js'; describe('Scoped Instances', () => { let testingModule: TestingModule; @@ -22,6 +21,10 @@ describe('Scoped Instances', () => { }).compile(); }); + afterEach(async () => { + await testingModule.close(); + }); + it('should dynamically resolve transient provider', async () => { const contextId = createContextId(); const transient1 = await testingModule.resolve(TransientService, contextId); @@ -30,10 +33,10 @@ describe('Scoped Instances', () => { TRANSIENT_SCOPED_FACTORY, ); - expect(transient1).to.be.instanceOf(TransientService); - expect(transient2).to.be.instanceOf(TransientService); - expect(transient1).to.be.equal(transient2); - expect(transientFactory).to.be.true; + expect(transient1).toBeInstanceOf(TransientService); + expect(transient2).toBeInstanceOf(TransientService); + expect(transient1).toBe(transient2); + expect(transientFactory).toBe(true); }); it('should dynamically resolve nested transient provider', async () => { @@ -47,8 +50,8 @@ describe('Scoped Instances', () => { contextId, ); - expect(transientTwoDepthLevel.svc.logger).to.not.be.undefined; - expect(transientThreeDepthLevel.svc.svc.logger).to.not.be.undefined; + expect(transientTwoDepthLevel.svc.logger).not.toBeUndefined(); + expect(transientThreeDepthLevel.svc.svc.logger).not.toBeUndefined(); }); it('should dynamically resolve request-scoped provider', async () => { @@ -62,11 +65,11 @@ describe('Scoped Instances', () => { const request3 = await testingModule.resolve(ScopedService, ctxId); const requestFactory = await testingModule.resolve(REQUEST_SCOPED_FACTORY); - expect(request1).to.be.instanceOf(ScopedService); - expect(request2).to.be.instanceOf(ScopedService); - expect(request3).to.not.be.equal(request2); - expect(requestFactory).to.be.true; - expect(request3.request).to.be.equal(requestProvider); + expect(request1).toBeInstanceOf(ScopedService); + expect(request2).toBeInstanceOf(ScopedService); + expect(request3).not.toBe(request2); + expect(requestFactory).toBe(true); + expect(request3.request).toBe(requestProvider); }); it('should dynamically resolve request-scoped controller', async () => { @@ -74,24 +77,19 @@ describe('Scoped Instances', () => { const request2 = await testingModule.resolve(ScopedController); const request3 = await testingModule.resolve(ScopedController, { id: 1 }); - expect(request1).to.be.instanceOf(ScopedController); - expect(request2).to.be.instanceOf(ScopedController); - expect(request3).to.not.be.equal(request2); + expect(request1).toBeInstanceOf(ScopedController); + expect(request2).toBeInstanceOf(ScopedController); + expect(request3).not.toBe(request2); }); it('should throw an exception when "get()" method is used for scoped providers', () => { - try { - testingModule.get(ScopedController); - } catch (err) { - expect(err).to.be.instanceOf(InvalidClassScopeException); - } + expect(() => testingModule.get(ScopedController)).toThrow( + InvalidClassScopeException, + ); }); - it('should throw an exception when "resolve()" method is used for static providers', async () => { - try { - await testingModule.resolve(STATIC_FACTORY); - } catch (err) { - expect(err).to.be.instanceOf(InvalidClassScopeException); - } + it('should resolve static providers via "resolve()" method', async () => { + const result = await testingModule.resolve(STATIC_FACTORY); + expect(result).toBe(true); }); }); diff --git a/integration/injector/src/app.module.ts b/integration/injector/src/app.module.ts index 881a3758f51..81687ddc84a 100644 --- a/integration/injector/src/app.module.ts +++ b/integration/injector/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { ExportsModule } from './exports/exports.module'; +import { ExportsModule } from './exports/exports.module.js'; @Module({ imports: [ExportsModule], diff --git a/integration/injector/src/circular-modules/circular.module.ts b/integration/injector/src/circular-modules/circular.module.ts index bb6afb42a38..6eac28bfd3b 100644 --- a/integration/injector/src/circular-modules/circular.module.ts +++ b/integration/injector/src/circular-modules/circular.module.ts @@ -1,6 +1,6 @@ import { Module, forwardRef } from '@nestjs/common'; -import { CircularService } from './circular.service'; -import { InputModule } from './input.module'; +import { CircularService } from './circular.service.js'; +import { InputModule } from './input.module.js'; @Module({ imports: [forwardRef(() => InputModule)], diff --git a/integration/injector/src/circular-modules/circular.service.ts b/integration/injector/src/circular-modules/circular.service.ts index 546d63e6eab..c4dda4019dc 100644 --- a/integration/injector/src/circular-modules/circular.service.ts +++ b/integration/injector/src/circular-modules/circular.service.ts @@ -1,10 +1,14 @@ -import { Injectable, forwardRef, Inject } from '@nestjs/common'; -import { InputService } from './input.service'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; + +// Use a lazy reference to avoid ESM circular-import TDZ issues +// with emitDecoratorMetadata. +let InputServiceRef: any; +void import('./input.service.js').then(m => (InputServiceRef = m.InputService)); @Injectable() export class CircularService { constructor( - @Inject(forwardRef(() => InputService)) - public readonly service: InputService, + @Inject(forwardRef(() => InputServiceRef)) + public readonly service: any, ) {} } diff --git a/integration/injector/src/circular-modules/input.module.ts b/integration/injector/src/circular-modules/input.module.ts index c76dcaa85ec..3766b298767 100644 --- a/integration/injector/src/circular-modules/input.module.ts +++ b/integration/injector/src/circular-modules/input.module.ts @@ -1,6 +1,6 @@ import { Module, forwardRef } from '@nestjs/common'; -import { CircularModule } from './circular.module'; -import { InputService } from './input.service'; +import { CircularModule } from './circular.module.js'; +import { InputService } from './input.service.js'; @Module({ imports: [forwardRef(() => CircularModule)], diff --git a/integration/injector/src/circular-modules/input.service.ts b/integration/injector/src/circular-modules/input.service.ts index 342e8bf0d0f..344f438eff4 100644 --- a/integration/injector/src/circular-modules/input.service.ts +++ b/integration/injector/src/circular-modules/input.service.ts @@ -1,10 +1,17 @@ -import { Injectable, Inject, forwardRef } from '@nestjs/common'; -import { CircularService } from './circular.service'; +import { Inject, Injectable, forwardRef } from '@nestjs/common'; + +// Use a lazy reference to avoid ESM circular-import TDZ issues +// with emitDecoratorMetadata. The class is imported asynchronously +// and cached for forwardRef resolution. +let CircularServiceRef: any; +void import('./circular.service.js').then( + m => (CircularServiceRef = m.CircularService), +); @Injectable() export class InputService { constructor( - @Inject(forwardRef(() => CircularService)) - public readonly service: CircularService, + @Inject(forwardRef(() => CircularServiceRef)) + public readonly service: any, ) {} } diff --git a/integration/injector/src/circular-properties/circular-properties.module.ts b/integration/injector/src/circular-properties/circular-properties.module.ts index 6af6be61a99..84b58362a22 100644 --- a/integration/injector/src/circular-properties/circular-properties.module.ts +++ b/integration/injector/src/circular-properties/circular-properties.module.ts @@ -1,6 +1,6 @@ import { forwardRef, Module } from '@nestjs/common'; -import { CircularService } from './circular.service'; -import { InputPropertiesModule } from './input-properties.module'; +import { CircularService } from './circular.service.js'; +import { InputPropertiesModule } from './input-properties.module.js'; @Module({ imports: [forwardRef(() => InputPropertiesModule)], diff --git a/integration/injector/src/circular-properties/circular.service.ts b/integration/injector/src/circular-properties/circular.service.ts index 6baa26e7860..f27cb71a280 100644 --- a/integration/injector/src/circular-properties/circular.service.ts +++ b/integration/injector/src/circular-properties/circular.service.ts @@ -1,8 +1,12 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { InputService } from './input.service'; + +// Lazy reference to avoid ESM circular-import TDZ issues +// with emitDecoratorMetadata. +let InputServiceRef: any; +void import('./input.service.js').then(m => (InputServiceRef = m.InputService)); @Injectable() export class CircularService { - @Inject(forwardRef(() => InputService)) - public readonly service: InputService; + @Inject(forwardRef(() => InputServiceRef)) + public readonly service: any; } diff --git a/integration/injector/src/circular-properties/input-properties.module.ts b/integration/injector/src/circular-properties/input-properties.module.ts index e2662ed0df7..821d1935d48 100644 --- a/integration/injector/src/circular-properties/input-properties.module.ts +++ b/integration/injector/src/circular-properties/input-properties.module.ts @@ -1,6 +1,6 @@ import { forwardRef, Module } from '@nestjs/common'; -import { CircularPropertiesModule } from './circular-properties.module'; -import { InputService } from './input.service'; +import { CircularPropertiesModule } from './circular-properties.module.js'; +import { InputService } from './input.service.js'; @Module({ imports: [forwardRef(() => CircularPropertiesModule)], diff --git a/integration/injector/src/circular-properties/input.service.ts b/integration/injector/src/circular-properties/input.service.ts index 225b7e7ddcf..d44f72716fe 100644 --- a/integration/injector/src/circular-properties/input.service.ts +++ b/integration/injector/src/circular-properties/input.service.ts @@ -1,8 +1,14 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { CircularService } from './circular.service'; + +// Lazy reference to avoid ESM circular-import TDZ issues +// with emitDecoratorMetadata. +let CircularServiceRef: any; +void import('./circular.service.js').then( + m => (CircularServiceRef = m.CircularService), +); @Injectable() export class InputService { - @Inject(forwardRef(() => CircularService)) - public readonly service: CircularService; + @Inject(forwardRef(() => CircularServiceRef)) + public readonly service: any; } diff --git a/integration/injector/src/circular-structure-dynamic-module/circular.module.ts b/integration/injector/src/circular-structure-dynamic-module/circular.module.ts index 6172fb2c5be..0dc6de4c581 100644 --- a/integration/injector/src/circular-structure-dynamic-module/circular.module.ts +++ b/integration/injector/src/circular-structure-dynamic-module/circular.module.ts @@ -1,5 +1,5 @@ import { DynamicModule } from '@nestjs/common'; -import { InputService } from './input.service'; +import { InputService } from './input.service.js'; export class CircularModule { static forRoot(): DynamicModule { diff --git a/integration/injector/src/circular/circular.module.ts b/integration/injector/src/circular/circular.module.ts index b0f1cf4510c..460887479a8 100644 --- a/integration/injector/src/circular/circular.module.ts +++ b/integration/injector/src/circular/circular.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CircularService } from './circular.service'; -import { InputService } from './input.service'; +import { CircularService } from './circular.service.js'; +import { InputService } from './input.service.js'; @Module({ providers: [CircularService, InputService], diff --git a/integration/injector/src/circular/circular.service.ts b/integration/injector/src/circular/circular.service.ts index 546d63e6eab..883eda44c7a 100644 --- a/integration/injector/src/circular/circular.service.ts +++ b/integration/injector/src/circular/circular.service.ts @@ -1,10 +1,14 @@ -import { Injectable, forwardRef, Inject } from '@nestjs/common'; -import { InputService } from './input.service'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; + +// Lazy reference to avoid ESM circular-import TDZ issues +// with emitDecoratorMetadata. +let InputServiceRef: any; +void import('./input.service.js').then(m => (InputServiceRef = m.InputService)); @Injectable() export class CircularService { constructor( - @Inject(forwardRef(() => InputService)) - public readonly service: InputService, + @Inject(forwardRef(() => InputServiceRef)) + public readonly service: any, ) {} } diff --git a/integration/injector/src/circular/input.service.ts b/integration/injector/src/circular/input.service.ts index 342e8bf0d0f..8a4c8251684 100644 --- a/integration/injector/src/circular/input.service.ts +++ b/integration/injector/src/circular/input.service.ts @@ -1,10 +1,16 @@ -import { Injectable, Inject, forwardRef } from '@nestjs/common'; -import { CircularService } from './circular.service'; +import { Inject, Injectable, forwardRef } from '@nestjs/common'; + +// Lazy reference to avoid ESM circular-import TDZ issues +// with emitDecoratorMetadata. +let CircularServiceRef: any; +void import('./circular.service.js').then( + m => (CircularServiceRef = m.CircularService), +); @Injectable() export class InputService { constructor( - @Inject(forwardRef(() => CircularService)) - public readonly service: CircularService, + @Inject(forwardRef(() => CircularServiceRef)) + public readonly service: any, ) {} } diff --git a/integration/injector/src/defaults/defaults.module.ts b/integration/injector/src/defaults/defaults.module.ts index f8a7ddb9f59..504561615a1 100644 --- a/integration/injector/src/defaults/defaults.module.ts +++ b/integration/injector/src/defaults/defaults.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { DefaultsService } from './defaults.service'; +import { DefaultsService } from './defaults.service.js'; @Module({ providers: [DefaultsService], diff --git a/integration/injector/src/defaults/defaults.service.ts b/integration/injector/src/defaults/defaults.service.ts index edc4790f1b1..6a9a51fd587 100644 --- a/integration/injector/src/defaults/defaults.service.ts +++ b/integration/injector/src/defaults/defaults.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable, Optional } from '@nestjs/common'; -import { CoreService } from './core.service'; +import { CoreService } from './core.service.js'; @Injectable() export class DefaultsService { diff --git a/integration/injector/src/exports/exports.module.ts b/integration/injector/src/exports/exports.module.ts index 00190030b4e..7757fee7ec0 100644 --- a/integration/injector/src/exports/exports.module.ts +++ b/integration/injector/src/exports/exports.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { ExportsService } from './exports.service'; +import { ExportsService } from './exports.service.js'; @Module({ exports: [ExportsService], diff --git a/integration/injector/src/inject/inject.module.ts b/integration/injector/src/inject/inject.module.ts index aee4f6c11cb..07578c82b16 100644 --- a/integration/injector/src/inject/inject.module.ts +++ b/integration/injector/src/inject/inject.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { InjectService } from './inject.service'; +import { InjectService } from './inject.service.js'; @Module({ providers: [InjectService], diff --git a/integration/injector/src/inject/inject.service.ts b/integration/injector/src/inject/inject.service.ts index 36dc8b4ed4d..b2d37d5a395 100644 --- a/integration/injector/src/inject/inject.service.ts +++ b/integration/injector/src/inject/inject.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { CoreService } from './core.service'; +import { CoreService } from './core.service.js'; @Injectable() export class InjectService { diff --git a/integration/injector/src/multiple-providers/multiple-providers.module.ts b/integration/injector/src/multiple-providers/multiple-providers.module.ts index 1bb2345eeec..73a9f113d3d 100644 --- a/integration/injector/src/multiple-providers/multiple-providers.module.ts +++ b/integration/injector/src/multiple-providers/multiple-providers.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { AModule } from './a.module'; -import { BModule } from './b.module'; -import { CModule } from './c.module'; +import { AModule } from './a.module.js'; +import { BModule } from './b.module.js'; +import { CModule } from './c.module.js'; @Module({ imports: [AModule, BModule, CModule], diff --git a/integration/injector/src/properties/properties.module.ts b/integration/injector/src/properties/properties.module.ts index b2ad36ff3b4..a9ab6d24105 100644 --- a/integration/injector/src/properties/properties.module.ts +++ b/integration/injector/src/properties/properties.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { DependencyService } from './dependency.service'; -import { PropertiesService, SYMBOL_TOKEN } from './properties.service'; +import { DependencyService } from './dependency.service.js'; +import { PropertiesService, SYMBOL_TOKEN } from './properties.service.js'; @Module({ providers: [ diff --git a/integration/injector/src/properties/properties.service.ts b/integration/injector/src/properties/properties.service.ts index cd450752440..4211b828647 100644 --- a/integration/injector/src/properties/properties.service.ts +++ b/integration/injector/src/properties/properties.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DependencyService } from './dependency.service'; +import { DependencyService } from './dependency.service.js'; export const SYMBOL_TOKEN = Symbol('token'); diff --git a/integration/injector/src/scoped/scoped.module.ts b/integration/injector/src/scoped/scoped.module.ts index 0b2e0bb9270..9e2dc7e6633 100644 --- a/integration/injector/src/scoped/scoped.module.ts +++ b/integration/injector/src/scoped/scoped.module.ts @@ -1,9 +1,9 @@ import { Module, Scope } from '@nestjs/common'; -import { ScopedController } from './scoped.controller'; -import { ScopedService } from './scoped.service'; -import { TransientService } from './transient.service'; -import { Transient2Service } from './transient2.service'; -import { Transient3Service } from './transient3.service'; +import { ScopedController } from './scoped.controller.js'; +import { ScopedService } from './scoped.service.js'; +import { TransientService } from './transient.service.js'; +import { Transient2Service } from './transient2.service.js'; +import { Transient3Service } from './transient3.service.js'; export const STATIC_FACTORY = 'STATIC_FACTORY'; export const REQUEST_SCOPED_FACTORY = 'REQUEST_SCOPED_FACTORY'; diff --git a/integration/injector/src/scoped/transient.service.ts b/integration/injector/src/scoped/transient.service.ts index db496f8c427..e61ab3a03cd 100644 --- a/integration/injector/src/scoped/transient.service.ts +++ b/integration/injector/src/scoped/transient.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { Transient2Service } from './transient2.service'; +import { Transient2Service } from './transient2.service.js'; @Injectable({ scope: Scope.TRANSIENT }) export class TransientService { diff --git a/integration/injector/src/scoped/transient3.service.ts b/integration/injector/src/scoped/transient3.service.ts index fa11ba9c2aa..2b51ae604f8 100644 --- a/integration/injector/src/scoped/transient3.service.ts +++ b/integration/injector/src/scoped/transient3.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { TransientService } from './transient.service'; +import { TransientService } from './transient.service.js'; @Injectable({ scope: Scope.TRANSIENT }) export class Transient3Service { diff --git a/integration/injector/tsconfig.json b/integration/injector/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/injector/tsconfig.json +++ b/integration/injector/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/inspector/e2e/fixtures/post-init-graph.json b/integration/inspector/e2e/fixtures/post-init-graph.json index cf3f5ec86a4..1ab96f5f168 100644 --- a/integration/inspector/e2e/fixtures/post-init-graph.json +++ b/integration/inspector/e2e/fixtures/post-init-graph.json @@ -2596,68 +2596,6 @@ } }, "entrypoints": { - "1131051184": [ - { - "type": "websocket", - "methodName": "create", - "className": "ChatGateway", - "classNodeId": "1131051184", - "metadata": { - "port": 0, - "key": "createChat", - "message": "createChat" - }, - "id": "1131051184_create" - }, - { - "type": "websocket", - "methodName": "findAll", - "className": "ChatGateway", - "classNodeId": "1131051184", - "metadata": { - "port": 0, - "key": "findAllChat", - "message": "findAllChat" - }, - "id": "1131051184_findAll" - }, - { - "type": "websocket", - "methodName": "findOne", - "className": "ChatGateway", - "classNodeId": "1131051184", - "metadata": { - "port": 0, - "key": "findOneChat", - "message": "findOneChat" - }, - "id": "1131051184_findOne" - }, - { - "type": "websocket", - "methodName": "update", - "className": "ChatGateway", - "classNodeId": "1131051184", - "metadata": { - "port": 0, - "key": "updateChat", - "message": "updateChat" - }, - "id": "1131051184_update" - }, - { - "type": "websocket", - "methodName": "remove", - "className": "ChatGateway", - "classNodeId": "1131051184", - "metadata": { - "port": 0, - "key": "removeChat", - "message": "removeChat" - }, - "id": "1131051184_remove" - } - ], "1472486160": [ { "type": "http-endpoint", diff --git a/integration/inspector/e2e/graph-inspector.spec.ts b/integration/inspector/e2e/graph-inspector.spec.ts index 9c02de6902c..7cfc489d25b 100644 --- a/integration/inspector/e2e/graph-inspector.spec.ts +++ b/integration/inspector/e2e/graph-inspector.spec.ts @@ -1,21 +1,21 @@ import { ValidationPipe } from '@nestjs/common'; -import { Injector } from '@nestjs/core/injector/injector'; -import { SerializedGraph } from '@nestjs/core/inspector/serialized-graph'; +import { Injector } from '@nestjs/core/injector/injector.js'; +import { SerializedGraph } from '@nestjs/core/inspector/serialized-graph.js'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; import { readFileSync } from 'fs'; import { join } from 'path'; -import * as sinon from 'sinon'; -import { AppModule } from '../src/app.module'; -import { HttpExceptionFilter } from '../src/common/filters/http-exception.filter'; -import { TimeoutInterceptor } from '../src/common/interceptors/timeout.interceptor'; +import { AppModule } from '../src/app.module.js'; +import { HttpExceptionFilter } from '../src/common/filters/http-exception.filter.js'; +import { TimeoutInterceptor } from '../src/common/interceptors/timeout.interceptor.js'; describe('Graph inspector', () => { let testingModule: TestingModule; - before(async () => { - sinon.stub(Injector.prototype as any, 'getNowTimestamp').callsFake(() => 0); + beforeAll(async () => { + vi.spyOn(Injector.prototype as any, 'getNowTimestamp').mockImplementation( + () => 0, + ); testingModule = await Test.createTestingModule({ imports: [AppModule], @@ -27,16 +27,16 @@ describe('Graph inspector', () => { // Update snapshot: // writeFileSync( - // join(__dirname, 'fixtures', 'pre-init-graph.json'), + // join(import.meta.dirname, 'fixtures', 'pre-init-graph.json'), // graph.toString(), // ); const snapshot = readFileSync( - join(__dirname, 'fixtures', 'pre-init-graph.json'), + join(import.meta.dirname, 'fixtures', 'pre-init-graph.json'), 'utf-8', ); - expect(JSON.parse(graph.toString())).to.deep.equal(JSON.parse(snapshot)); + expect(JSON.parse(graph.toString())).toEqual(JSON.parse(snapshot)); }); it('should generate a post-initialization graph and match snapshot', async () => { @@ -55,15 +55,15 @@ describe('Graph inspector', () => { // Update snapshot: // writeFileSync( - // join(__dirname, 'fixtures', 'post-init-graph.json'), + // join(import.meta.dirname, 'fixtures', 'post-init-graph.json'), // graph.toString(), // ); const snapshot = readFileSync( - join(__dirname, 'fixtures', 'post-init-graph.json'), + join(import.meta.dirname, 'fixtures', 'post-init-graph.json'), 'utf-8', ); - expect(graph.toString()).to.equal(snapshot); + expect(graph.toString()).toBe(snapshot); }); }); diff --git a/integration/inspector/src/app.module.ts b/integration/inspector/src/app.module.ts index 1d395b9bf45..8d07f9f7133 100644 --- a/integration/inspector/src/app.module.ts +++ b/integration/inspector/src/app.module.ts @@ -1,19 +1,19 @@ import { Module, Scope } from '@nestjs/common'; -import { AppV1Controller } from './app-v1.controller'; -import { AppV2Controller } from './app-v2.controller'; -import { CatsModule } from './cats/cats.module'; -import { ChatModule } from './chat/chat.module'; -import { HelloModule as CircularHelloModule } from './circular-hello/hello.module'; -import { HelloService } from './circular-hello/hello.service'; -import { InputModule } from './circular-modules/input.module'; -import { CoreModule } from './core/core.module'; -import { DatabaseModule } from './database/database.module'; -import { DogsModule } from './dogs/dogs.module'; -import { DurableModule } from './durable/durable.module'; -import { ExternalSvcModule } from './external-svc/external-svc.module'; -import { PropertiesModule } from './properties/properties.module'; -import { RequestChainModule } from './request-chain/request-chain.module'; -import { UsersModule } from './users/users.module'; +import { AppV1Controller } from './app-v1.controller.js'; +import { AppV2Controller } from './app-v2.controller.js'; +import { CatsModule } from './cats/cats.module.js'; +import { ChatModule } from './chat/chat.module.js'; +import { HelloModule as CircularHelloModule } from './circular-hello/hello.module.js'; +import { HelloService } from './circular-hello/hello.service.js'; +import { InputModule } from './circular-modules/input.module.js'; +import { CoreModule } from './core/core.module.js'; +import { DatabaseModule } from './database/database.module.js'; +import { DogsModule } from './dogs/dogs.module.js'; +import { DurableModule } from './durable/durable.module.js'; +import { ExternalSvcModule } from './external-svc/external-svc.module.js'; +import { PropertiesModule } from './properties/properties.module.js'; +import { RequestChainModule } from './request-chain/request-chain.module.js'; +import { UsersModule } from './users/users.module.js'; class Meta { static COUNTER = 0; diff --git a/integration/inspector/src/cats/cats.controller.ts b/integration/inspector/src/cats/cats.controller.ts index b9f929a21e0..2e1b27c0970 100644 --- a/integration/inspector/src/cats/cats.controller.ts +++ b/integration/inspector/src/cats/cats.controller.ts @@ -1,9 +1,9 @@ import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common'; -import { RolesGuard } from '../common/guards/roles.guard'; -import { ParseIntPipe } from '../common/pipes/parse-int.pipe'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './interfaces/cat.interface'; +import { RolesGuard } from '../common/guards/roles.guard.js'; +import { ParseIntPipe } from '../common/pipes/parse-int.pipe.js'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; @UseGuards(RolesGuard) @Controller('cats') diff --git a/integration/inspector/src/cats/cats.module.ts b/integration/inspector/src/cats/cats.module.ts index f3291c7d11e..14b21cd9f14 100644 --- a/integration/inspector/src/cats/cats.module.ts +++ b/integration/inspector/src/cats/cats.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; @Module({ controllers: [CatsController], diff --git a/integration/inspector/src/cats/cats.service.ts b/integration/inspector/src/cats/cats.service.ts index 2619cd7176d..dc5f51e15ec 100644 --- a/integration/inspector/src/cats/cats.service.ts +++ b/integration/inspector/src/cats/cats.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Cat } from './interfaces/cat.interface'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable() export class CatsService { diff --git a/integration/inspector/src/chat/chat.gateway.ts b/integration/inspector/src/chat/chat.gateway.ts index 75e6c7c28d3..cdbd5315579 100644 --- a/integration/inspector/src/chat/chat.gateway.ts +++ b/integration/inspector/src/chat/chat.gateway.ts @@ -3,9 +3,9 @@ import { SubscribeMessage, MessageBody, } from '@nestjs/websockets'; -import { ChatService } from './chat.service'; -import { CreateChatDto } from './dto/create-chat.dto'; -import { UpdateChatDto } from './dto/update-chat.dto'; +import { ChatService } from './chat.service.js'; +import { CreateChatDto } from './dto/create-chat.dto.js'; +import { UpdateChatDto } from './dto/update-chat.dto.js'; @WebSocketGateway() export class ChatGateway { diff --git a/integration/inspector/src/chat/chat.module.ts b/integration/inspector/src/chat/chat.module.ts index d3ad301f587..4a267e1923b 100644 --- a/integration/inspector/src/chat/chat.module.ts +++ b/integration/inspector/src/chat/chat.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { ChatService } from './chat.service'; -import { ChatGateway } from './chat.gateway'; +import { ChatService } from './chat.service.js'; +import { ChatGateway } from './chat.gateway.js'; @Module({ providers: [ChatGateway, ChatService], diff --git a/integration/inspector/src/chat/chat.service.ts b/integration/inspector/src/chat/chat.service.ts index 1fa0676de00..711f6f7ce32 100644 --- a/integration/inspector/src/chat/chat.service.ts +++ b/integration/inspector/src/chat/chat.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { CreateChatDto } from './dto/create-chat.dto'; -import { UpdateChatDto } from './dto/update-chat.dto'; +import { CreateChatDto } from './dto/create-chat.dto.js'; +import { UpdateChatDto } from './dto/update-chat.dto.js'; @Injectable() export class ChatService { diff --git a/integration/inspector/src/chat/dto/update-chat.dto.ts b/integration/inspector/src/chat/dto/update-chat.dto.ts index 0a372f66617..6fd98b4c594 100644 --- a/integration/inspector/src/chat/dto/update-chat.dto.ts +++ b/integration/inspector/src/chat/dto/update-chat.dto.ts @@ -1,5 +1,5 @@ import { PartialType } from '@nestjs/mapped-types'; -import { CreateChatDto } from './create-chat.dto'; +import { CreateChatDto } from './create-chat.dto.js'; export class UpdateChatDto extends PartialType(CreateChatDto) { id: number; diff --git a/integration/inspector/src/circular-hello/hello.controller.ts b/integration/inspector/src/circular-hello/hello.controller.ts index 5b4af7bf215..94d20f26cf3 100644 --- a/integration/inspector/src/circular-hello/hello.controller.ts +++ b/integration/inspector/src/circular-hello/hello.controller.ts @@ -5,11 +5,11 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { Guard } from './guards/request-scoped.guard'; -import { HelloService } from './hello.service'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UserByIdPipe } from './users/user-by-id.pipe'; -import { UsersService } from './users/users.service'; +import { Guard } from './guards/request-scoped.guard.js'; +import { HelloService } from './hello.service.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; +import { UsersService } from './users/users.service.js'; @Controller('hello') export class HelloController { diff --git a/integration/inspector/src/circular-hello/hello.module.ts b/integration/inspector/src/circular-hello/hello.module.ts index 1a0d0bbfd7c..c0dc1995c66 100644 --- a/integration/inspector/src/circular-hello/hello.module.ts +++ b/integration/inspector/src/circular-hello/hello.module.ts @@ -1,7 +1,7 @@ import { DynamicModule, Inject, Module, Provider } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloService } from './hello.service'; -import { UsersService } from './users/users.service'; +import { HelloController } from './hello.controller.js'; +import { HelloService } from './hello.service.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HelloController], diff --git a/integration/inspector/src/circular-hello/users/user-by-id.pipe.ts b/integration/inspector/src/circular-hello/users/user-by-id.pipe.ts index 2b81cb6fbb5..b63f924559f 100644 --- a/integration/inspector/src/circular-hello/users/user-by-id.pipe.ts +++ b/integration/inspector/src/circular-hello/users/user-by-id.pipe.ts @@ -1,5 +1,5 @@ import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Injectable() export class UserByIdPipe implements PipeTransform { diff --git a/integration/inspector/src/circular-modules/circular.module.ts b/integration/inspector/src/circular-modules/circular.module.ts index bb6afb42a38..6eac28bfd3b 100644 --- a/integration/inspector/src/circular-modules/circular.module.ts +++ b/integration/inspector/src/circular-modules/circular.module.ts @@ -1,6 +1,6 @@ import { Module, forwardRef } from '@nestjs/common'; -import { CircularService } from './circular.service'; -import { InputModule } from './input.module'; +import { CircularService } from './circular.service.js'; +import { InputModule } from './input.module.js'; @Module({ imports: [forwardRef(() => InputModule)], diff --git a/integration/inspector/src/circular-modules/circular.service.ts b/integration/inspector/src/circular-modules/circular.service.ts index 546d63e6eab..4aee1a2bf40 100644 --- a/integration/inspector/src/circular-modules/circular.service.ts +++ b/integration/inspector/src/circular-modules/circular.service.ts @@ -1,5 +1,5 @@ import { Injectable, forwardRef, Inject } from '@nestjs/common'; -import { InputService } from './input.service'; +import { InputService } from './input.service.js'; @Injectable() export class CircularService { diff --git a/integration/inspector/src/circular-modules/input.module.ts b/integration/inspector/src/circular-modules/input.module.ts index c76dcaa85ec..3766b298767 100644 --- a/integration/inspector/src/circular-modules/input.module.ts +++ b/integration/inspector/src/circular-modules/input.module.ts @@ -1,6 +1,6 @@ import { Module, forwardRef } from '@nestjs/common'; -import { CircularModule } from './circular.module'; -import { InputService } from './input.service'; +import { CircularModule } from './circular.module.js'; +import { InputService } from './input.service.js'; @Module({ imports: [forwardRef(() => CircularModule)], diff --git a/integration/inspector/src/circular-modules/input.service.ts b/integration/inspector/src/circular-modules/input.service.ts index 342e8bf0d0f..56dc82c99dc 100644 --- a/integration/inspector/src/circular-modules/input.service.ts +++ b/integration/inspector/src/circular-modules/input.service.ts @@ -1,5 +1,5 @@ import { Injectable, Inject, forwardRef } from '@nestjs/common'; -import { CircularService } from './circular.service'; +import { CircularService } from './circular.service.js'; @Injectable() export class InputService { diff --git a/integration/inspector/src/core/core.module.ts b/integration/inspector/src/core/core.module.ts index 5bc1990b1c0..c489ebacf49 100644 --- a/integration/inspector/src/core/core.module.ts +++ b/integration/inspector/src/core/core.module.ts @@ -1,9 +1,9 @@ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; -import { CatsController } from '../cats/cats.controller'; -import { LoggerMiddleware } from '../common/middleware/logger.middleware'; -import { LoggingInterceptor } from './interceptors/logging.interceptor'; -import { TransformInterceptor } from './interceptors/transform.interceptor'; +import { CatsController } from '../cats/cats.controller.js'; +import { LoggerMiddleware } from '../common/middleware/logger.middleware.js'; +import { LoggingInterceptor } from './interceptors/logging.interceptor.js'; +import { TransformInterceptor } from './interceptors/transform.interceptor.js'; @Module({ providers: [ diff --git a/integration/inspector/src/database/database.controller.ts b/integration/inspector/src/database/database.controller.ts index 629762ae0b7..c474a78396e 100644 --- a/integration/inspector/src/database/database.controller.ts +++ b/integration/inspector/src/database/database.controller.ts @@ -7,9 +7,9 @@ import { Param, Delete, } from '@nestjs/common'; -import { DatabaseService } from './database.service'; -import { CreateDatabaseDto } from './dto/create-database.dto'; -import { UpdateDatabaseDto } from './dto/update-database.dto'; +import { DatabaseService } from './database.service.js'; +import { CreateDatabaseDto } from './dto/create-database.dto.js'; +import { UpdateDatabaseDto } from './dto/update-database.dto.js'; @Controller('database') export class DatabaseController { diff --git a/integration/inspector/src/database/database.module.ts b/integration/inspector/src/database/database.module.ts index 2f37f674b74..6d45e0900d0 100644 --- a/integration/inspector/src/database/database.module.ts +++ b/integration/inspector/src/database/database.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { DatabaseService } from './database.service'; -import { DatabaseController } from './database.controller'; +import { DatabaseService } from './database.service.js'; +import { DatabaseController } from './database.controller.js'; @Module({ controllers: [DatabaseController], diff --git a/integration/inspector/src/database/database.service.ts b/integration/inspector/src/database/database.service.ts index 84566789511..e55638f1814 100644 --- a/integration/inspector/src/database/database.service.ts +++ b/integration/inspector/src/database/database.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { CreateDatabaseDto } from './dto/create-database.dto'; -import { UpdateDatabaseDto } from './dto/update-database.dto'; +import { CreateDatabaseDto } from './dto/create-database.dto.js'; +import { UpdateDatabaseDto } from './dto/update-database.dto.js'; @Injectable() export class DatabaseService { diff --git a/integration/inspector/src/database/dto/update-database.dto.ts b/integration/inspector/src/database/dto/update-database.dto.ts index 5cc66666510..01350102d55 100644 --- a/integration/inspector/src/database/dto/update-database.dto.ts +++ b/integration/inspector/src/database/dto/update-database.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from '@nestjs/mapped-types'; -import { CreateDatabaseDto } from './create-database.dto'; +import { CreateDatabaseDto } from './create-database.dto.js'; export class UpdateDatabaseDto extends PartialType(CreateDatabaseDto) {} diff --git a/integration/inspector/src/defaults/defaults.module.ts b/integration/inspector/src/defaults/defaults.module.ts index f8a7ddb9f59..504561615a1 100644 --- a/integration/inspector/src/defaults/defaults.module.ts +++ b/integration/inspector/src/defaults/defaults.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { DefaultsService } from './defaults.service'; +import { DefaultsService } from './defaults.service.js'; @Module({ providers: [DefaultsService], diff --git a/integration/inspector/src/defaults/defaults.service.ts b/integration/inspector/src/defaults/defaults.service.ts index edc4790f1b1..6a9a51fd587 100644 --- a/integration/inspector/src/defaults/defaults.service.ts +++ b/integration/inspector/src/defaults/defaults.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable, Optional } from '@nestjs/common'; -import { CoreService } from './core.service'; +import { CoreService } from './core.service.js'; @Injectable() export class DefaultsService { diff --git a/integration/inspector/src/dogs/dogs.controller.ts b/integration/inspector/src/dogs/dogs.controller.ts index 8d5ab7d8ab9..79f2244469c 100644 --- a/integration/inspector/src/dogs/dogs.controller.ts +++ b/integration/inspector/src/dogs/dogs.controller.ts @@ -7,9 +7,9 @@ import { Param, Delete, } from '@nestjs/common'; -import { DogsService } from './dogs.service'; -import { CreateDogDto } from './dto/create-dog.dto'; -import { UpdateDogDto } from './dto/update-dog.dto'; +import { DogsService } from './dogs.service.js'; +import { CreateDogDto } from './dto/create-dog.dto.js'; +import { UpdateDogDto } from './dto/update-dog.dto.js'; @Controller('dogs') export class DogsController { diff --git a/integration/inspector/src/dogs/dogs.module.ts b/integration/inspector/src/dogs/dogs.module.ts index 3385a39e303..c1da22cc2bd 100644 --- a/integration/inspector/src/dogs/dogs.module.ts +++ b/integration/inspector/src/dogs/dogs.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { DogsService } from './dogs.service'; -import { DogsController } from './dogs.controller'; +import { DogsService } from './dogs.service.js'; +import { DogsController } from './dogs.controller.js'; @Module({ controllers: [DogsController], diff --git a/integration/inspector/src/dogs/dogs.service.ts b/integration/inspector/src/dogs/dogs.service.ts index f1f2d93474c..3993bc49f5a 100644 --- a/integration/inspector/src/dogs/dogs.service.ts +++ b/integration/inspector/src/dogs/dogs.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { CreateDogDto } from './dto/create-dog.dto'; -import { UpdateDogDto } from './dto/update-dog.dto'; +import { CreateDogDto } from './dto/create-dog.dto.js'; +import { UpdateDogDto } from './dto/update-dog.dto.js'; @Injectable() export class DogsService { diff --git a/integration/inspector/src/dogs/dto/update-dog.dto.ts b/integration/inspector/src/dogs/dto/update-dog.dto.ts index 15d4939c8c4..fbd24ed0ab8 100644 --- a/integration/inspector/src/dogs/dto/update-dog.dto.ts +++ b/integration/inspector/src/dogs/dto/update-dog.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from '@nestjs/mapped-types'; -import { CreateDogDto } from './create-dog.dto'; +import { CreateDogDto } from './create-dog.dto.js'; export class UpdateDogDto extends PartialType(CreateDogDto) {} diff --git a/integration/inspector/src/durable/durable.controller.ts b/integration/inspector/src/durable/durable.controller.ts index be51ad2dfea..7a59f6905e3 100644 --- a/integration/inspector/src/durable/durable.controller.ts +++ b/integration/inspector/src/durable/durable.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from '@nestjs/common'; -import { DurableService } from './durable.service'; +import { DurableService } from './durable.service.js'; @Controller('durable') export class DurableController { diff --git a/integration/inspector/src/durable/durable.module.ts b/integration/inspector/src/durable/durable.module.ts index ed312900f28..a7939600c52 100644 --- a/integration/inspector/src/durable/durable.module.ts +++ b/integration/inspector/src/durable/durable.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { DurableController } from './durable.controller'; -import { DurableService } from './durable.service'; +import { DurableController } from './durable.controller.js'; +import { DurableService } from './durable.service.js'; @Module({ controllers: [DurableController], diff --git a/integration/inspector/src/external-svc/dto/update-external-svc.dto.ts b/integration/inspector/src/external-svc/dto/update-external-svc.dto.ts index 95be362d9ec..de5938cc5a2 100644 --- a/integration/inspector/src/external-svc/dto/update-external-svc.dto.ts +++ b/integration/inspector/src/external-svc/dto/update-external-svc.dto.ts @@ -1,5 +1,5 @@ import { PartialType } from '@nestjs/mapped-types'; -import { CreateExternalSvcDto } from './create-external-svc.dto'; +import { CreateExternalSvcDto } from './create-external-svc.dto.js'; export class UpdateExternalSvcDto extends PartialType(CreateExternalSvcDto) { id: number; diff --git a/integration/inspector/src/external-svc/external-svc.controller.ts b/integration/inspector/src/external-svc/external-svc.controller.ts index e1e9e224748..58fda87a696 100644 --- a/integration/inspector/src/external-svc/external-svc.controller.ts +++ b/integration/inspector/src/external-svc/external-svc.controller.ts @@ -1,8 +1,8 @@ import { Controller } from '@nestjs/common'; import { MessagePattern, Payload } from '@nestjs/microservices'; -import { CreateExternalSvcDto } from './dto/create-external-svc.dto'; -import { UpdateExternalSvcDto } from './dto/update-external-svc.dto'; -import { ExternalSvcService } from './external-svc.service'; +import { CreateExternalSvcDto } from './dto/create-external-svc.dto.js'; +import { UpdateExternalSvcDto } from './dto/update-external-svc.dto.js'; +import { ExternalSvcService } from './external-svc.service.js'; @Controller() export class ExternalSvcController { diff --git a/integration/inspector/src/external-svc/external-svc.module.ts b/integration/inspector/src/external-svc/external-svc.module.ts index f4b57fc02c3..4ea43e85c7f 100644 --- a/integration/inspector/src/external-svc/external-svc.module.ts +++ b/integration/inspector/src/external-svc/external-svc.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { ExternalSvcService } from './external-svc.service'; -import { ExternalSvcController } from './external-svc.controller'; +import { ExternalSvcService } from './external-svc.service.js'; +import { ExternalSvcController } from './external-svc.controller.js'; @Module({ controllers: [ExternalSvcController], diff --git a/integration/inspector/src/external-svc/external-svc.service.ts b/integration/inspector/src/external-svc/external-svc.service.ts index 9620630c2fd..b8b48567d8a 100644 --- a/integration/inspector/src/external-svc/external-svc.service.ts +++ b/integration/inspector/src/external-svc/external-svc.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { CreateExternalSvcDto } from './dto/create-external-svc.dto'; -import { UpdateExternalSvcDto } from './dto/update-external-svc.dto'; +import { CreateExternalSvcDto } from './dto/create-external-svc.dto.js'; +import { UpdateExternalSvcDto } from './dto/update-external-svc.dto.js'; @Injectable() export class ExternalSvcService { diff --git a/integration/inspector/src/properties/properties.module.ts b/integration/inspector/src/properties/properties.module.ts index b2ad36ff3b4..a9ab6d24105 100644 --- a/integration/inspector/src/properties/properties.module.ts +++ b/integration/inspector/src/properties/properties.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { DependencyService } from './dependency.service'; -import { PropertiesService, SYMBOL_TOKEN } from './properties.service'; +import { DependencyService } from './dependency.service.js'; +import { PropertiesService, SYMBOL_TOKEN } from './properties.service.js'; @Module({ providers: [ diff --git a/integration/inspector/src/properties/properties.service.ts b/integration/inspector/src/properties/properties.service.ts index cd450752440..4211b828647 100644 --- a/integration/inspector/src/properties/properties.service.ts +++ b/integration/inspector/src/properties/properties.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DependencyService } from './dependency.service'; +import { DependencyService } from './dependency.service.js'; export const SYMBOL_TOKEN = Symbol('token'); diff --git a/integration/inspector/src/request-chain/helper/helper.module.ts b/integration/inspector/src/request-chain/helper/helper.module.ts index 1c154ea44cb..fa31021e32d 100644 --- a/integration/inspector/src/request-chain/helper/helper.module.ts +++ b/integration/inspector/src/request-chain/helper/helper.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { HelperService } from './helper.service'; +import { HelperService } from './helper.service.js'; @Module({ providers: [HelperService], diff --git a/integration/inspector/src/request-chain/interceptors/logging.interceptor.ts b/integration/inspector/src/request-chain/interceptors/logging.interceptor.ts index 008a6999373..17c83c7e82d 100644 --- a/integration/inspector/src/request-chain/interceptors/logging.interceptor.ts +++ b/integration/inspector/src/request-chain/interceptors/logging.interceptor.ts @@ -5,7 +5,7 @@ import { NestInterceptor, } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { HelperService } from '../helper/helper.service'; +import { HelperService } from '../helper/helper.service.js'; @Injectable() export class LoggingInterceptor implements NestInterceptor { diff --git a/integration/inspector/src/request-chain/request-chain.controller.ts b/integration/inspector/src/request-chain/request-chain.controller.ts index 03813352ddb..f6b22f1caef 100644 --- a/integration/inspector/src/request-chain/request-chain.controller.ts +++ b/integration/inspector/src/request-chain/request-chain.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, UseInterceptors } from '@nestjs/common'; -import { LoggingInterceptor } from './interceptors/logging.interceptor'; -import { RequestChainService } from './request-chain.service'; +import { LoggingInterceptor } from './interceptors/logging.interceptor.js'; +import { RequestChainService } from './request-chain.service.js'; @Controller('hello') export class RequestChainController { diff --git a/integration/inspector/src/request-chain/request-chain.module.ts b/integration/inspector/src/request-chain/request-chain.module.ts index eba8f451d9f..e5478eef781 100644 --- a/integration/inspector/src/request-chain/request-chain.module.ts +++ b/integration/inspector/src/request-chain/request-chain.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { HelperModule } from './helper/helper.module'; -import { RequestChainController } from './request-chain.controller'; -import { RequestChainService } from './request-chain.service'; +import { HelperModule } from './helper/helper.module.js'; +import { RequestChainController } from './request-chain.controller.js'; +import { RequestChainService } from './request-chain.service.js'; @Module({ imports: [HelperModule], diff --git a/integration/inspector/src/request-chain/request-chain.service.ts b/integration/inspector/src/request-chain/request-chain.service.ts index c8c00068d3f..bf526aa498f 100644 --- a/integration/inspector/src/request-chain/request-chain.service.ts +++ b/integration/inspector/src/request-chain/request-chain.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { HelperService } from './helper/helper.service'; +import { HelperService } from './helper/helper.service.js'; @Injectable() export class RequestChainService { diff --git a/integration/inspector/src/users/dto/update-user.dto.ts b/integration/inspector/src/users/dto/update-user.dto.ts index dfd37fb1edb..912cdc528b4 100644 --- a/integration/inspector/src/users/dto/update-user.dto.ts +++ b/integration/inspector/src/users/dto/update-user.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from '@nestjs/mapped-types'; -import { CreateUserDto } from './create-user.dto'; +import { CreateUserDto } from './create-user.dto.js'; export class UpdateUserDto extends PartialType(CreateUserDto) {} diff --git a/integration/inspector/src/users/users.controller.ts b/integration/inspector/src/users/users.controller.ts index d5db3a65e56..435e5438ee7 100644 --- a/integration/inspector/src/users/users.controller.ts +++ b/integration/inspector/src/users/users.controller.ts @@ -7,9 +7,9 @@ import { Param, Delete, } from '@nestjs/common'; -import { UsersService } from './users.service'; -import { CreateUserDto } from './dto/create-user.dto'; -import { UpdateUserDto } from './dto/update-user.dto'; +import { UsersService } from './users.service.js'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { UpdateUserDto } from './dto/update-user.dto.js'; @Controller('users') export class UsersController { diff --git a/integration/inspector/src/users/users.module.ts b/integration/inspector/src/users/users.module.ts index ecca17ad64b..f7b0c0f955c 100644 --- a/integration/inspector/src/users/users.module.ts +++ b/integration/inspector/src/users/users.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { UsersService } from './users.service'; -import { UsersController } from './users.controller'; +import { UsersService } from './users.service.js'; +import { UsersController } from './users.controller.js'; @Module({ controllers: [UsersController], diff --git a/integration/inspector/src/users/users.service.ts b/integration/inspector/src/users/users.service.ts index 0a55903da13..a27a61a12e9 100644 --- a/integration/inspector/src/users/users.service.ts +++ b/integration/inspector/src/users/users.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { CreateUserDto } from './dto/create-user.dto'; -import { UpdateUserDto } from './dto/update-user.dto'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { UpdateUserDto } from './dto/update-user.dto.js'; @Injectable() export class UsersService { diff --git a/integration/inspector/tsconfig.json b/integration/inspector/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/inspector/tsconfig.json +++ b/integration/inspector/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/lazy-modules/e2e/lazy-import-global-modules.spec.ts b/integration/lazy-modules/e2e/lazy-import-global-modules.spec.ts index 7bb41fb4e54..b23e5ccb9df 100644 --- a/integration/lazy-modules/e2e/lazy-import-global-modules.spec.ts +++ b/integration/lazy-modules/e2e/lazy-import-global-modules.spec.ts @@ -1,10 +1,6 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as chai from 'chai'; -import { expect } from 'chai'; -import { AppModule } from '../src/app.module'; -import chaiAsPromised = require('chai-as-promised'); -chai.use(chaiAsPromised); +import { AppModule } from '../src/app.module.js'; describe('Lazy imports', () => { let app: INestApplication; @@ -18,7 +14,7 @@ describe('Lazy imports', () => { }); it(`should allow imports of global modules`, async () => { - await expect(app.init()).to.eventually.be.fulfilled; + await expect(app.init()).resolves.toBeDefined(); }); afterEach(async () => { diff --git a/integration/lazy-modules/e2e/lazy-import-request-providers.spec.ts b/integration/lazy-modules/e2e/lazy-import-request-providers.spec.ts index a437cf7413a..a79e24ee0c9 100644 --- a/integration/lazy-modules/e2e/lazy-import-request-providers.spec.ts +++ b/integration/lazy-modules/e2e/lazy-import-request-providers.spec.ts @@ -1,8 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { LazyController } from '../src/lazy.controller'; +import request from 'supertest'; +import { LazyController } from '../src/lazy.controller.js'; describe('Lazy Requested Scoped providers', () => { let app: INestApplication; @@ -19,12 +18,16 @@ describe('Lazy Requested Scoped providers', () => { it('should not recreate dependencies for default scope', async () => { const resultOne = await request(app.getHttpServer()).get('/lazy/request'); - expect(resultOne.text).to.be.equal('Hi! Counter is 1'); - expect(resultOne.statusCode).to.be.equal(200); + expect(resultOne.text).toBe('Hi! Counter is 1'); + expect(resultOne.statusCode).toBe(200); const resultTwo = await request(app.getHttpServer()).get('/lazy/request'); - expect(resultTwo.text).to.be.equal('Hi! Counter is 2'); - expect(resultTwo.statusCode).to.be.equal(200); + expect(resultTwo.text).toBe('Hi! Counter is 2'); + expect(resultTwo.statusCode).toBe(200); + }); + + afterEach(async () => { + await app.close(); }); }); diff --git a/integration/lazy-modules/e2e/lazy-import-transient-providers.spec.ts b/integration/lazy-modules/e2e/lazy-import-transient-providers.spec.ts index ce34a3347a6..2dd7bd137b4 100644 --- a/integration/lazy-modules/e2e/lazy-import-transient-providers.spec.ts +++ b/integration/lazy-modules/e2e/lazy-import-transient-providers.spec.ts @@ -1,10 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { TransientLazyModule } from '../src/transient.module'; -import { LazyController } from '../src/lazy.controller'; -import * as chai from 'chai'; -import { expect } from 'chai'; -import * as request from 'supertest'; +import request from 'supertest'; +import { LazyController } from '../src/lazy.controller.js'; describe('Lazy Transient providers', () => { let app: INestApplication; @@ -23,10 +20,14 @@ describe('Lazy Transient providers', () => { const resultTwo = await request(app.getHttpServer()).get('/lazy/transient'); - expect(resultOne.text).to.be.equal('Hi! Counter is 1'); - expect(resultOne.statusCode).to.be.equal(200); + expect(resultOne.text).toBe('Hi! Counter is 1'); + expect(resultOne.statusCode).toBe(200); - expect(resultTwo.text).to.be.equal('Hi! Counter is 2'); - expect(resultTwo.statusCode).to.be.equal(200); + expect(resultTwo.text).toBe('Hi! Counter is 2'); + expect(resultTwo.statusCode).toBe(200); + }); + + afterEach(async () => { + await app.close(); }); }); diff --git a/integration/lazy-modules/src/app.module.ts b/integration/lazy-modules/src/app.module.ts index 65d38dadcc9..53def0bb2eb 100644 --- a/integration/lazy-modules/src/app.module.ts +++ b/integration/lazy-modules/src/app.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { LazyModuleLoader } from '@nestjs/core'; -import { EagerModule } from './eager.module'; -import { GlobalModule } from './global.module'; -import { LazyModule } from './lazy.module'; +import { EagerModule } from './eager.module.js'; +import { GlobalModule } from './global.module.js'; +import { LazyModule } from './lazy.module.js'; @Module({ imports: [GlobalModule, EagerModule], diff --git a/integration/lazy-modules/src/eager.module.ts b/integration/lazy-modules/src/eager.module.ts index f50669ee92e..98892ca627a 100644 --- a/integration/lazy-modules/src/eager.module.ts +++ b/integration/lazy-modules/src/eager.module.ts @@ -1,5 +1,5 @@ import { Module, Injectable } from '@nestjs/common'; -import { GlobalService } from './global.module'; +import { GlobalService } from './global.module.js'; @Injectable() export class EagerService { diff --git a/integration/lazy-modules/src/lazy.controller.ts b/integration/lazy-modules/src/lazy.controller.ts index 45ab41bcf49..d70fb2a9c85 100644 --- a/integration/lazy-modules/src/lazy.controller.ts +++ b/integration/lazy-modules/src/lazy.controller.ts @@ -7,20 +7,20 @@ export class LazyController { @Get('transient') async exec() { - const { TransientLazyModule } = await import('./transient.module'); + const { TransientLazyModule } = await import('./transient.module.js'); const moduleRef = await this.lazyLoadModule.load(() => TransientLazyModule); - const { TransientService } = await import('./transient.service'); + const { TransientService } = await import('./transient.service.js'); const _service = await moduleRef.resolve(TransientService); return _service.eager(); } @Get('request') async execRequestScope() { - const { RequestLazyModule } = await import('./request.module'); + const { RequestLazyModule } = await import('./request.module.js'); const moduleRef = await this.lazyLoadModule.load(() => RequestLazyModule); - const { RequestService } = await import('./request.service'); + const { RequestService } = await import('./request.service.js'); const _service = await moduleRef.resolve(RequestService); return _service.eager(); diff --git a/integration/lazy-modules/src/lazy.module.ts b/integration/lazy-modules/src/lazy.module.ts index 69c056435e1..631b926a12e 100644 --- a/integration/lazy-modules/src/lazy.module.ts +++ b/integration/lazy-modules/src/lazy.module.ts @@ -1,5 +1,5 @@ import { Module, Injectable } from '@nestjs/common'; -import { GlobalService } from './global.module'; +import { GlobalService } from './global.module.js'; @Injectable() export class LazyService { diff --git a/integration/lazy-modules/src/main.ts b/integration/lazy-modules/src/main.ts index 23f3240c271..2061a1ced07 100644 --- a/integration/lazy-modules/src/main.ts +++ b/integration/lazy-modules/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/integration/lazy-modules/src/request.module.ts b/integration/lazy-modules/src/request.module.ts index 8cd549fced3..fae44cb8af9 100644 --- a/integration/lazy-modules/src/request.module.ts +++ b/integration/lazy-modules/src/request.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { EagerService } from './eager.module'; -import { GlobalService } from './global.module'; -import { RequestService } from './request.service'; +import { EagerService } from './eager.module.js'; +import { GlobalService } from './global.module.js'; +import { RequestService } from './request.service.js'; @Module({ imports: [], diff --git a/integration/lazy-modules/src/request.service.ts b/integration/lazy-modules/src/request.service.ts index 846b542c8cd..61bd018575d 100644 --- a/integration/lazy-modules/src/request.service.ts +++ b/integration/lazy-modules/src/request.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { EagerService } from './eager.module'; +import { EagerService } from './eager.module.js'; @Injectable({ scope: Scope.REQUEST }) export class RequestService { diff --git a/integration/lazy-modules/src/transient.module.ts b/integration/lazy-modules/src/transient.module.ts index b76a686fec9..c0d509ce002 100644 --- a/integration/lazy-modules/src/transient.module.ts +++ b/integration/lazy-modules/src/transient.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { GlobalService } from './global.module'; -import { EagerService } from './eager.module'; -import { TransientService } from './transient.service'; +import { GlobalService } from './global.module.js'; +import { EagerService } from './eager.module.js'; +import { TransientService } from './transient.service.js'; @Module({ imports: [], diff --git a/integration/lazy-modules/src/transient.service.ts b/integration/lazy-modules/src/transient.service.ts index fca76322860..e8585006ba8 100644 --- a/integration/lazy-modules/src/transient.service.ts +++ b/integration/lazy-modules/src/transient.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { EagerService } from './eager.module'; +import { EagerService } from './eager.module.js'; @Injectable({ scope: Scope.TRANSIENT }) export class TransientService { diff --git a/integration/lazy-modules/tsconfig.json b/integration/lazy-modules/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/lazy-modules/tsconfig.json +++ b/integration/lazy-modules/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/microservices/e2e/broadcast-mqtt.spec.ts b/integration/microservices/e2e/broadcast-mqtt.spec.ts index 641bf442ae3..08e7f5a0b8e 100644 --- a/integration/microservices/e2e/broadcast-mqtt.spec.ts +++ b/integration/microservices/e2e/broadcast-mqtt.spec.ts @@ -1,8 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { MqttBroadcastController } from '../src/mqtt/mqtt-broadcast.controller'; +import request from 'supertest'; +import { MqttBroadcastController } from '../src/mqtt/mqtt-broadcast.controller.js'; describe('MQTT transport', () => { let server; diff --git a/integration/microservices/e2e/broadcast-nats.spec.ts b/integration/microservices/e2e/broadcast-nats.spec.ts index b8cf69b50ec..24c7c25dec5 100644 --- a/integration/microservices/e2e/broadcast-nats.spec.ts +++ b/integration/microservices/e2e/broadcast-nats.spec.ts @@ -1,8 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { NatsBroadcastController } from '../src/nats/nats-broadcast.controller'; +import request from 'supertest'; +import { NatsBroadcastController } from '../src/nats/nats-broadcast.controller.js'; describe('NATS transport', () => { let server; diff --git a/integration/microservices/e2e/broadcast-redis.spec.ts b/integration/microservices/e2e/broadcast-redis.spec.ts index 1cf8690ef41..dab143ad715 100644 --- a/integration/microservices/e2e/broadcast-redis.spec.ts +++ b/integration/microservices/e2e/broadcast-redis.spec.ts @@ -1,8 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { RedisBroadcastController } from '../src/redis/redis-broadcast.controller'; +import request from 'supertest'; +import { RedisBroadcastController } from '../src/redis/redis-broadcast.controller.js'; describe('REDIS transport', () => { let server; diff --git a/integration/microservices/e2e/concurrent-kafka.spec.ts b/integration/microservices/e2e/concurrent-kafka.spec.ts index 11dd0dbb733..e3fdc037267 100644 --- a/integration/microservices/e2e/concurrent-kafka.spec.ts +++ b/integration/microservices/e2e/concurrent-kafka.spec.ts @@ -2,10 +2,10 @@ import { INestApplication, Logger } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; import { Admin, ITopicMetadata, Kafka } from 'kafkajs'; -import * as request from 'supertest'; +import request from 'supertest'; import * as util from 'util'; -import { KafkaConcurrentController } from '../src/kafka-concurrent/kafka-concurrent.controller'; -import { KafkaConcurrentMessagesController } from '../src/kafka-concurrent/kafka-concurrent.messages.controller'; +import { KafkaConcurrentController } from '../src/kafka-concurrent/kafka-concurrent.controller.js'; +import { KafkaConcurrentMessagesController } from '../src/kafka-concurrent/kafka-concurrent.messages.controller.js'; describe.skip('Kafka concurrent', function () { const numbersOfServers = 3; @@ -20,8 +20,6 @@ describe.skip('Kafka concurrent', function () { const logger = new Logger('concurrent-kafka.spec.ts'); // set timeout to be longer (especially for the after hook) - this.timeout(30000); - const startServer = async () => { const module = await Test.createTestingModule({ controllers: [ @@ -162,7 +160,7 @@ describe.skip('Kafka concurrent', function () { }); }), ); - }).timeout(30000); + }); it(`Concurrent messages without forcing a rebalance.`, async () => { // wait a second before notifying the servers to respond @@ -280,7 +278,7 @@ describe.skip('Kafka concurrent', function () { ); }); - after(`Stopping Kafka app`, async () => { + afterAll(async () => { // close all concurrently return Promise.all( apps.map(async app => { diff --git a/integration/microservices/e2e/disconnected-client.spec.ts b/integration/microservices/e2e/disconnected-client.spec.ts index 706c2e05918..e61d34389ac 100644 --- a/integration/microservices/e2e/disconnected-client.spec.ts +++ b/integration/microservices/e2e/disconnected-client.spec.ts @@ -1,9 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; +import request from 'supertest'; import { App } from 'supertest/types'; -import { DisconnectedClientController } from '../src/disconnected.controller'; +import { DisconnectedClientController } from '../src/disconnected.controller.js'; describe('Disconnected client', () => { let server: App; diff --git a/integration/microservices/e2e/fanout-exchange-rmq.spec.ts b/integration/microservices/e2e/fanout-exchange-rmq.spec.ts index c271f88bda1..4561ed8e326 100644 --- a/integration/microservices/e2e/fanout-exchange-rmq.spec.ts +++ b/integration/microservices/e2e/fanout-exchange-rmq.spec.ts @@ -1,9 +1,9 @@ import { INestApplication, INestMicroservice } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { RMQFanoutExchangeProducerController } from '../src/rmq/fanout-exchange-producer-rmq.controller'; -import { RMQFanoutExchangeConsumerController } from '../src/rmq/fanout-exchange-consumer-rmq.controller'; +import request from 'supertest'; +import { RMQFanoutExchangeProducerController } from '../src/rmq/fanout-exchange-producer-rmq.controller.js'; +import { RMQFanoutExchangeConsumerController } from '../src/rmq/fanout-exchange-consumer-rmq.controller.js'; describe('RabbitMQ transport (Fanout Exchange)', () => { let server: any; diff --git a/integration/microservices/e2e/math-grpc.spec.ts b/integration/microservices/e2e/math-grpc.spec.ts index 8a80ced27d5..acf722bf62f 100644 --- a/integration/microservices/e2e/math-grpc.spec.ts +++ b/integration/microservices/e2e/math-grpc.spec.ts @@ -4,21 +4,16 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; import { fail } from 'assert'; -import { expect, use } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; import { join } from 'path'; -import * as sinon from 'sinon'; -import * as request from 'supertest'; -import { GrpcController } from '../src/grpc/grpc.controller'; - -use(chaiAsPromised); +import request from 'supertest'; +import { GrpcController } from '../src/grpc/grpc.controller.js'; describe('GRPC transport', () => { let server; let app: INestApplication; let client: any; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ controllers: [GrpcController], }).compile(); @@ -31,8 +26,8 @@ describe('GRPC transport', () => { options: { package: ['math', 'math2'], protoPath: [ - join(__dirname, '../src/grpc/math.proto'), - join(__dirname, '../src/grpc/math2.proto'), + join(import.meta.dirname, '../src/grpc/math.proto'), + join(import.meta.dirname, '../src/grpc/math2.proto'), ], }, }); @@ -42,7 +37,7 @@ describe('GRPC transport', () => { await app.init(); // Load proto-buffers for test gRPC dispatch const proto = ProtoLoader.loadSync( - join(__dirname, '../src/grpc/math.proto'), + join(import.meta.dirname, '../src/grpc/math.proto'), ) as any; // Create Raw gRPC client object const protoGRPC = GRPC.loadPackageDefinition(proto) as any; @@ -93,7 +88,7 @@ describe('GRPC transport', () => { const callHandler = client.SumStream(); callHandler.on('data', (msg: number) => { - expect(msg).to.eql({ result: 15 }); + expect(msg).toEqual({ result: 15 }); callHandler.cancel(); }); @@ -115,7 +110,7 @@ describe('GRPC transport', () => { const callHandler = client.SumStreamPass(); callHandler.on('data', (msg: number) => { - expect(msg).to.eql({ result: 15 }); + expect(msg).toEqual({ result: 15 }); callHandler.cancel(); }); @@ -140,8 +135,6 @@ describe('GRPC transport', () => { // event. Prior to this test, a bug existed where the server would // send the incorrect number of messages due to improper backpressure // handling that wrote messages more than once. - this.timeout(10000); - const largeMessages = client.streamLargeMessages(); // [0, 1, 2, ..., 999] const expectedIds = Array.from({ length: 1000 }, (_, n) => n); @@ -151,16 +144,16 @@ describe('GRPC transport', () => { receivedIds.push(msg.id); }); - expect(receivedIds).to.deep.equal(expectedIds); + expect(receivedIds).toEqual(expectedIds); }); describe('streaming calls that error', () => { // We want to assert that the application does not crash when an error is encountered with an unhandledRejection // the best way to do that is to listen for the unhandledRejection event and fail the test if it is called - let processSpy: sinon.SinonSpy; + let processSpy: ReturnType; beforeEach(() => { - processSpy = sinon.spy(); + processSpy = vi.fn(); process.on('unhandledRejection', processSpy); }); @@ -189,16 +182,16 @@ describe('GRPC transport', () => { }); }); - await expect(call).to.eventually.be.rejectedWith( + await expect(call).rejects.toThrow( '3 INVALID_ARGUMENT: dividing by 0 is not possible', ); // if this fails the application has crashed - expect(processSpy.called).to.be.false; + expect(processSpy).not.toHaveBeenCalled(); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/microservices/e2e/mqtt-record-builder.spec.ts b/integration/microservices/e2e/mqtt-record-builder.spec.ts index 94d57d5bef6..3ebd93f4dbd 100644 --- a/integration/microservices/e2e/mqtt-record-builder.spec.ts +++ b/integration/microservices/e2e/mqtt-record-builder.spec.ts @@ -1,8 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { MqttController } from '../src/mqtt/mqtt.controller'; +import request from 'supertest'; +import { MqttController } from '../src/mqtt/mqtt.controller.js'; describe('MQTT transport', () => { let server; diff --git a/integration/microservices/e2e/orders-grpc.spec.ts b/integration/microservices/e2e/orders-grpc.spec.ts index e34864f91e2..daf6e43f082 100644 --- a/integration/microservices/e2e/orders-grpc.spec.ts +++ b/integration/microservices/e2e/orders-grpc.spec.ts @@ -5,18 +5,17 @@ import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { ExpressAdapter } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; import { fail } from 'assert'; -import { expect } from 'chai'; -import * as express from 'express'; +import express from 'express'; import { join } from 'path'; -import * as request from 'supertest'; -import { AdvancedGrpcController } from '../src/grpc-advanced/advanced.grpc.controller'; +import request from 'supertest'; +import { AdvancedGrpcController } from '../src/grpc-advanced/advanced.grpc.controller.js'; describe('Advanced GRPC transport', () => { let server; let app: INestApplication; let client: any; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ controllers: [AdvancedGrpcController], }).compile(); @@ -33,7 +32,9 @@ describe('Advanced GRPC transport', () => { package: 'proto_example', protoPath: 'root.proto', loader: { - includeDirs: [join(__dirname, '../src/grpc-advanced/proto')], + includeDirs: [ + join(import.meta.dirname, '../src/grpc-advanced/proto'), + ], keepCase: true, }, }, @@ -43,7 +44,7 @@ describe('Advanced GRPC transport', () => { await app.init(); // Load proto-buffers for test gRPC dispatch const proto = ProtoLoader.loadSync('root.proto', { - includeDirs: [join(__dirname, '../src/grpc-advanced/proto')], + includeDirs: [join(import.meta.dirname, '../src/grpc-advanced/proto')], }) as any; // Create Raw gRPC client object const protoGRPC = GRPC.loadPackageDefinition(proto) as any; @@ -93,8 +94,8 @@ describe('Advanced GRPC transport', () => { }, (err, result) => { // Compare results - expect(err).to.be.null; - expect(result).to.eql({ + expect(err).toBeNull(); + expect(result).toEqual({ id: 1, itemTypes: [1], shipmentType: { @@ -115,12 +116,12 @@ describe('Advanced GRPC transport', () => { // Get Set-Cookie from Metadata callHandler.on('metadata', (metadata: GRPC.Metadata) => { - expect(metadata.get('Set-Cookie')[0]).to.eq('test_cookie=abcd'); + expect(metadata.get('Set-Cookie')[0]).toBe('test_cookie=abcd'); }); callHandler.on('data', (msg: number) => { // Do deep comparison (to.eql) - expect(msg).to.eql({ + expect(msg).toEqual({ id: 1, itemTypes: [1], shipmentType: { @@ -152,7 +153,7 @@ describe('Advanced GRPC transport', () => { callHandler.on('data', (msg: number) => { // Do deep comparison (to.eql) - expect(msg).to.eql({ + expect(msg).toEqual({ id: 1, itemTypes: [1], shipmentType: { @@ -184,7 +185,7 @@ describe('Advanced GRPC transport', () => { if (err) { throw err; } - expect(res).to.eql({ + expect(res).toEqual({ id: 1, itemTypes: [1], shipmentType: { @@ -208,7 +209,7 @@ describe('Advanced GRPC transport', () => { if (err) { throw err; } - expect(res).to.eql({ + expect(res).toEqual({ id: 1, itemTypes: [1], shipmentType: { diff --git a/integration/microservices/e2e/sum-kafka.spec.ts b/integration/microservices/e2e/sum-kafka.spec.ts index 945210f87e8..4cb8fec161c 100644 --- a/integration/microservices/e2e/sum-kafka.spec.ts +++ b/integration/microservices/e2e/sum-kafka.spec.ts @@ -1,13 +1,12 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { BusinessDto } from '../src/kafka/dtos/business.dto'; -import { UserDto } from '../src/kafka/dtos/user.dto'; -import { UserEntity } from '../src/kafka/entities/user.entity'; -import { KafkaController } from '../src/kafka/kafka.controller'; -import { KafkaMessagesController } from '../src/kafka/kafka.messages.controller'; +import request from 'supertest'; +import { BusinessDto } from '../src/kafka/dtos/business.dto.js'; +import { UserDto } from '../src/kafka/dtos/user.dto.js'; +import { UserEntity } from '../src/kafka/entities/user.entity.js'; +import { KafkaController } from '../src/kafka/kafka.controller.js'; +import { KafkaMessagesController } from '../src/kafka/kafka.messages.controller.js'; /** * Skip this flaky test in CI/CD pipeline as it frequently @@ -18,10 +17,7 @@ describe.skip('Kafka transport', function () { let app: INestApplication; // set timeout to be longer (especially for the after hook) - this.timeout(50000); - this.retries(10); - - before(`Start Kafka app`, async function () { + beforeAll(async () => { const module = await Test.createTestingModule({ controllers: [KafkaController, KafkaMessagesController], }).compile(); @@ -90,17 +86,18 @@ describe.skip('Kafka transport', function () { .expect(200, '15'); }); - it(`/POST (async event notification)`, done => { - void request(server) - .post('/notify') - .send() - .end(() => { - setTimeout(() => { - expect(KafkaController.IS_NOTIFIED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (async event notification)`, () => + new Promise(done => { + void request(server) + .post('/notify') + .send() + .end(() => { + setTimeout(() => { + expect(KafkaController.IS_NOTIFIED).toBe(true); + done(); + }, 1000); + }); + })); const userDto: UserDto = { email: 'enriquebenavidesm@gmail.com', @@ -132,7 +129,7 @@ describe.skip('Kafka transport', function () { await Promise.all(promises); }); - after(`Stopping Kafka app`, async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/microservices/e2e/sum-mqtt.spec.ts b/integration/microservices/e2e/sum-mqtt.spec.ts index d1a457bcd41..525d5036d15 100644 --- a/integration/microservices/e2e/sum-mqtt.spec.ts +++ b/integration/microservices/e2e/sum-mqtt.spec.ts @@ -1,9 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { MqttController } from '../src/mqtt/mqtt.controller'; +import request from 'supertest'; +import { MqttController } from '../src/mqtt/mqtt.controller.js'; describe('MQTT transport', () => { let server; @@ -47,7 +46,7 @@ describe('MQTT transport', () => { .post('/?command=streamSum') .send([1, 2, 3, 4, 5]) .expect(200, '15'); - }).timeout(5000); + }); it(`/POST (concurrent)`, function () { return request(server) @@ -65,38 +64,40 @@ describe('MQTT transport', () => { Array.from({ length: 10 }, (v, k) => k + 91), ]) .expect(200, 'true'); - }).timeout(5000); + }); it(`/POST (streaming)`, () => { return request(server) .post('/stream') .send([1, 2, 3, 4, 5]) .expect(200, '15'); - }).timeout(5000); - - it(`/POST (event notification)`, done => { - void request(server) - .post('/notify') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(MqttController.IS_NOTIFIED).to.be.true; - done(); - }, 1000); - }); }); - it(`/POST (wildcard EVENT #)`, done => { - void request(server) - .post('/wildcard-event') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(MqttController.IS_WILDCARD_EVENT_RECEIVED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (event notification)`, () => + new Promise(done => { + void request(server) + .post('/notify') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(MqttController.IS_NOTIFIED).toBe(true); + done(); + }, 1000); + }); + })); + + it(`/POST (wildcard EVENT #)`, () => + new Promise(done => { + void request(server) + .post('/wildcard-event') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(MqttController.IS_WILDCARD_EVENT_RECEIVED).toBe(true); + done(); + }, 1000); + }); + })); it(`/POST (wildcard MESSAGE #)`, () => { return request(server) @@ -105,17 +106,18 @@ describe('MQTT transport', () => { .expect(201, '15'); }); - it(`/POST (wildcard EVENT +)`, done => { - void request(server) - .post('/wildcard-event2') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(MqttController.IS_WILDCARD2_EVENT_RECEIVED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (wildcard EVENT +)`, () => + new Promise(done => { + void request(server) + .post('/wildcard-event2') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(MqttController.IS_WILDCARD2_EVENT_RECEIVED).toBe(true); + done(); + }, 1000); + }); + })); it(`/POST (wildcard MESSAGE +)`, () => { return request(server) @@ -124,17 +126,18 @@ describe('MQTT transport', () => { .expect(201, '15'); }); - it(`/POST (shared wildcard EVENT #)`, done => { - void request(server) - .post('/shared-wildcard-event') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(MqttController.IS_SHARED_WILDCARD_EVENT_RECEIVED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (shared wildcard EVENT #)`, () => + new Promise(done => { + void request(server) + .post('/shared-wildcard-event') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(MqttController.IS_SHARED_WILDCARD_EVENT_RECEIVED).toBe(true); + done(); + }, 1000); + }); + })); it(`/POST (shared wildcard MESSAGE #)`, () => { return request(server) @@ -143,17 +146,20 @@ describe('MQTT transport', () => { .expect(201, '15'); }); - it(`/POST (shared wildcard EVENT +)`, done => { - void request(server) - .post('/shared-wildcard-event2') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(MqttController.IS_SHARED_WILDCARD2_EVENT_RECEIVED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (shared wildcard EVENT +)`, () => + new Promise(done => { + void request(server) + .post('/shared-wildcard-event2') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(MqttController.IS_SHARED_WILDCARD2_EVENT_RECEIVED).toBe( + true, + ); + done(); + }, 1000); + }); + })); it(`/POST (shared wildcard MESSAGE +)`, () => { return request(server) diff --git a/integration/microservices/e2e/sum-nats.spec.ts b/integration/microservices/e2e/sum-nats.spec.ts index 99c8548edf1..939430d6773 100644 --- a/integration/microservices/e2e/sum-nats.spec.ts +++ b/integration/microservices/e2e/sum-nats.spec.ts @@ -1,10 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { NatsController } from '../src/nats/nats.controller'; -import { NatsService } from '../src/nats/nats.service'; +import request from 'supertest'; +import { NatsController } from '../src/nats/nats.controller.js'; +import { NatsService } from '../src/nats/nats.service.js'; describe('NATS transport', () => { let server; @@ -83,18 +82,19 @@ describe('NATS transport', () => { }); }); - it(`/POST (event notification)`, done => { - void request(server) - .post('/notify') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(NatsController.IS_NOTIFIED).to.be.true; - expect(NatsController.IS_NOTIFIED2).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (event notification)`, () => + new Promise(done => { + void request(server) + .post('/notify') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(NatsController.IS_NOTIFIED).toBe(true); + expect(NatsController.IS_NOTIFIED2).toBe(true); + done(); + }, 1000); + }); + })); it(`/POST (sending headers with "RecordBuilder")`, () => { const payload = { items: [1, 2, 3] }; diff --git a/integration/microservices/e2e/sum-redis.spec.ts b/integration/microservices/e2e/sum-redis.spec.ts index 246c2e2709d..201cc8f8e89 100644 --- a/integration/microservices/e2e/sum-redis.spec.ts +++ b/integration/microservices/e2e/sum-redis.spec.ts @@ -1,9 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { RedisController } from '../src/redis/redis.controller'; +import request from 'supertest'; +import { RedisController } from '../src/redis/redis.controller.js'; describe('REDIS transport', () => { let server; @@ -51,8 +50,6 @@ describe('REDIS transport', () => { }); it(`/POST (concurrent)`, function () { - this.retries(10); - return request(server) .post('/concurrent') .send([ @@ -68,7 +65,7 @@ describe('REDIS transport', () => { Array.from({ length: 10 }, (v, k) => k + 91), ]) .expect(200, 'true'); - }).timeout(5000); + }); it(`/POST (streaming)`, () => { return request(server) @@ -77,17 +74,18 @@ describe('REDIS transport', () => { .expect(200, '15'); }); - it(`/POST (event notification)`, done => { - void request(server) - .post('/notify') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(RedisController.IS_NOTIFIED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (event notification)`, () => + new Promise(done => { + void request(server) + .post('/notify') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(RedisController.IS_NOTIFIED).toBe(true); + done(); + }, 1000); + }); + })); afterEach(async () => { await app.close(); diff --git a/integration/microservices/e2e/sum-rmq.spec.ts b/integration/microservices/e2e/sum-rmq.spec.ts index 02ffa1db83c..d5dc57ba9f8 100644 --- a/integration/microservices/e2e/sum-rmq.spec.ts +++ b/integration/microservices/e2e/sum-rmq.spec.ts @@ -1,9 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { RMQController } from '../src/rmq/rmq.controller'; +import request from 'supertest'; +import { RMQController } from '../src/rmq/rmq.controller.js'; describe('RabbitMQ transport', () => { let server; @@ -82,20 +81,21 @@ describe('RabbitMQ transport', () => { .post('/multiple-urls') .send([1, 2, 3, 4, 5]) .expect(200, '15'); - }).timeout(10000); - - it(`/POST (event notification)`, done => { - void request(server) - .post('/notify') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(RMQController.IS_NOTIFIED).to.be.true; - done(); - }, 1000); - }); }); + it(`/POST (event notification)`, () => + new Promise(done => { + void request(server) + .post('/notify') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(RMQController.IS_NOTIFIED).toBe(true); + done(); + }, 1000); + }); + })); + it(`/POST (sending options with "RecordBuilder")`, () => { const payload = { items: [1, 2, 3] }; return request(server) diff --git a/integration/microservices/e2e/sum-rpc-async.spec.ts b/integration/microservices/e2e/sum-rpc-async.spec.ts index f6ead5b2485..998b8b3976c 100644 --- a/integration/microservices/e2e/sum-rpc-async.spec.ts +++ b/integration/microservices/e2e/sum-rpc-async.spec.ts @@ -15,7 +15,6 @@ import { TcpOptions, Transport, } from '@nestjs/microservices'; -import { expect } from 'chai'; let port: number; @@ -80,17 +79,18 @@ describe('RPC Async transport', () => { client = app.get('RPC_CLIENT', { strict: false }); }); - it(`/POST`, done => { - let retData = 0; - client.send({ cmd: 'sum' }, [1, 2, 3, 4, 5]).subscribe({ - next: val => (retData += val), - error: done, - complete: () => { - expect(retData).to.eq(15); - done(); - }, - }); - }); + it(`/POST`, () => + new Promise(done => { + let retData = 0; + client.send({ cmd: 'sum' }, [1, 2, 3, 4, 5]).subscribe({ + next: val => (retData += val), + error: done, + complete: () => { + expect(retData).toBe(15); + done(); + }, + }); + })); afterEach(async () => { await app.close(); diff --git a/integration/microservices/e2e/sum-rpc-tls.spec.ts b/integration/microservices/e2e/sum-rpc-tls.spec.ts index 91c31626d00..f8179cd020e 100644 --- a/integration/microservices/e2e/sum-rpc-tls.spec.ts +++ b/integration/microservices/e2e/sum-rpc-tls.spec.ts @@ -1,12 +1,11 @@ import { INestApplication } from '@nestjs/common'; import { Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import * as fs from 'fs'; import * as path from 'path'; -import * as request from 'supertest'; -import { AppController } from '../src/tcp-tls/app.controller'; -import { ApplicationModule } from '../src/tcp-tls/app.module'; +import request from 'supertest'; +import { AppController } from '../src/tcp-tls/app.controller.js'; +import { ApplicationModule } from '../src/tcp-tls/app.module.js'; describe('RPC TLS transport', () => { let server; @@ -14,13 +13,19 @@ describe('RPC TLS transport', () => { let key: string; let cert: string; - before(() => { + beforeAll(() => { // Generate a self-signed key pair key = fs - .readFileSync(path.join(__dirname, '../src/tcp-tls/privkey.pem'), 'utf8') + .readFileSync( + path.join(import.meta.dirname, '../src/tcp-tls/privkey.pem'), + 'utf8', + ) .toString(); cert = fs - .readFileSync(path.join(__dirname, '../src/tcp-tls/ca.cert.pem'), 'utf8') + .readFileSync( + path.join(import.meta.dirname, '../src/tcp-tls/ca.cert.pem'), + 'utf8', + ) .toString(); }); @@ -108,17 +113,18 @@ describe('RPC TLS transport', () => { return request(server).post('/?command=test').expect(500); }); - it(`/POST (event notification)`, done => { - void request(server) - .post('/notify') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(AppController.IS_NOTIFIED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (event notification)`, () => + new Promise(done => { + void request(server) + .post('/notify') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(AppController.IS_NOTIFIED).toBe(true); + done(); + }, 1000); + }); + })); it('/POST (custom client)', () => { return request(server) diff --git a/integration/microservices/e2e/sum-rpc.spec.ts b/integration/microservices/e2e/sum-rpc.spec.ts index 8e98fdceb4f..a39ed6d7377 100644 --- a/integration/microservices/e2e/sum-rpc.spec.ts +++ b/integration/microservices/e2e/sum-rpc.spec.ts @@ -1,10 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { AppController } from '../src/app.controller'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppController } from '../src/app.controller.js'; +import { AppModule } from '../src/app.module.js'; describe('RPC transport', () => { let server; @@ -93,17 +92,18 @@ describe('RPC transport', () => { return request(server).post('/?command=test').expect(500); }); - it(`/POST (event notification)`, done => { - void request(server) - .post('/notify') - .send([1, 2, 3, 4, 5]) - .end(() => { - setTimeout(() => { - expect(AppController.IS_NOTIFIED).to.be.true; - done(); - }, 1000); - }); - }); + it(`/POST (event notification)`, () => + new Promise(done => { + void request(server) + .post('/notify') + .send([1, 2, 3, 4, 5]) + .end(() => { + setTimeout(() => { + expect(AppController.IS_NOTIFIED).toBe(true); + done(); + }, 1000); + }); + })); it('/POST (custom client)', () => { return request(server) diff --git a/integration/microservices/e2e/topic-exchange-rmq.spec.ts b/integration/microservices/e2e/topic-exchange-rmq.spec.ts index 2a2bd1ff68e..d7b9ae34584 100644 --- a/integration/microservices/e2e/topic-exchange-rmq.spec.ts +++ b/integration/microservices/e2e/topic-exchange-rmq.spec.ts @@ -1,8 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { RMQTopicExchangeController } from '../src/rmq/topic-exchange-rmq.controller'; +import request from 'supertest'; +import { RMQTopicExchangeController } from '../src/rmq/topic-exchange-rmq.controller.js'; describe('RabbitMQ transport (Topic Exchange - wildcards)', () => { let server: any; diff --git a/integration/microservices/src/app.module.ts b/integration/microservices/src/app.module.ts index dacf646e45e..3100e7f28f4 100644 --- a/integration/microservices/src/app.module.ts +++ b/integration/microservices/src/app.module.ts @@ -1,5 +1,5 @@ import { Module, Injectable } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; import { ClientsModule, Transport, diff --git a/integration/microservices/src/disconnected.controller.ts b/integration/microservices/src/disconnected.controller.ts index fed84ce6f55..ebf8005631f 100644 --- a/integration/microservices/src/disconnected.controller.ts +++ b/integration/microservices/src/disconnected.controller.ts @@ -26,7 +26,8 @@ export class DisconnectedClientController { code === 'CONN_ERR' || code === 'ENOTFOUND' || code === 'CONNECTION_REFUSED' || - error.message.includes('Connection is closed.') + error.message.includes('Connection is closed.') || + error.message.includes('connection refused') ? new RequestTimeoutException('ECONNREFUSED') : new InternalServerErrorException(), ); diff --git a/integration/microservices/src/grpc-advanced/advanced.grpc.controller.ts b/integration/microservices/src/grpc-advanced/advanced.grpc.controller.ts index e4aa0234b3f..98e760e678f 100644 --- a/integration/microservices/src/grpc-advanced/advanced.grpc.controller.ts +++ b/integration/microservices/src/grpc-advanced/advanced.grpc.controller.ts @@ -23,7 +23,7 @@ export class AdvancedGrpcController { package: 'proto_example.orders', protoPath: 'root.proto', loader: { - includeDirs: [join(__dirname, './proto')], + includeDirs: [join(import.meta.dirname, './proto')], keepCase: true, }, }, diff --git a/integration/microservices/src/grpc/grpc.controller.ts b/integration/microservices/src/grpc/grpc.controller.ts index 8d435f18b36..757206f2873 100644 --- a/integration/microservices/src/grpc/grpc.controller.ts +++ b/integration/microservices/src/grpc/grpc.controller.ts @@ -24,7 +24,7 @@ export class GrpcController { constructor() { this.customClient = new ErrorHandlingProxy({ package: 'math', - protoPath: join(__dirname, 'math.proto'), + protoPath: join(import.meta.dirname, 'math.proto'), }); } @@ -32,7 +32,7 @@ export class GrpcController { transport: Transport.GRPC, options: { package: 'math', - protoPath: join(__dirname, 'math.proto'), + protoPath: join(import.meta.dirname, 'math.proto'), }, }) client: ClientGrpc; @@ -42,8 +42,8 @@ export class GrpcController { options: { package: ['math', 'math2'], protoPath: [ - join(__dirname, 'math.proto'), - join(__dirname, 'math2.proto'), + join(import.meta.dirname, 'math.proto'), + join(import.meta.dirname, 'math2.proto'), ], }, }) diff --git a/integration/microservices/src/kafka-concurrent/kafka-concurrent.controller.ts b/integration/microservices/src/kafka-concurrent/kafka-concurrent.controller.ts index 4816b4f13b2..dd2476b6443 100644 --- a/integration/microservices/src/kafka-concurrent/kafka-concurrent.controller.ts +++ b/integration/microservices/src/kafka-concurrent/kafka-concurrent.controller.ts @@ -10,7 +10,7 @@ import { import { Client, ClientKafka, Transport } from '@nestjs/microservices'; import { PartitionerArgs } from 'kafkajs'; import { Observable } from 'rxjs'; -import { SumDto } from './dto/sum.dto'; +import { SumDto } from './dto/sum.dto.js'; /** * The following function explicitly sends messages to the key representing the partition. diff --git a/integration/microservices/src/kafka/dtos/business.dto.ts b/integration/microservices/src/kafka/dtos/business.dto.ts index 15f82a83788..55130dfb1e4 100644 --- a/integration/microservices/src/kafka/dtos/business.dto.ts +++ b/integration/microservices/src/kafka/dtos/business.dto.ts @@ -1,4 +1,4 @@ -import { UserEntity } from '../entities/user.entity'; +import { UserEntity } from '../entities/user.entity.js'; export class BusinessDto { name: string; diff --git a/integration/microservices/src/kafka/entities/business.entity.ts b/integration/microservices/src/kafka/entities/business.entity.ts index 5e8feaea114..f96f71787c7 100644 --- a/integration/microservices/src/kafka/entities/business.entity.ts +++ b/integration/microservices/src/kafka/entities/business.entity.ts @@ -1,5 +1,5 @@ -import { UserEntity } from './user.entity'; -import { BusinessDto } from '../dtos/business.dto'; +import { UserEntity } from './user.entity.js'; +import { BusinessDto } from '../dtos/business.dto.js'; export class BusinessEntity { constructor(business: BusinessDto) { diff --git a/integration/microservices/src/kafka/entities/user.entity.ts b/integration/microservices/src/kafka/entities/user.entity.ts index 6ad630980f0..16277050d45 100644 --- a/integration/microservices/src/kafka/entities/user.entity.ts +++ b/integration/microservices/src/kafka/entities/user.entity.ts @@ -1,4 +1,4 @@ -import { UserDto } from '../dtos/user.dto'; +import { UserDto } from '../dtos/user.dto.js'; export class UserEntity { constructor(user: UserDto) { diff --git a/integration/microservices/src/kafka/kafka.controller.ts b/integration/microservices/src/kafka/kafka.controller.ts index 0db49ebcd67..3595cdbe189 100644 --- a/integration/microservices/src/kafka/kafka.controller.ts +++ b/integration/microservices/src/kafka/kafka.controller.ts @@ -9,8 +9,8 @@ import { } from '@nestjs/common'; import { Client, ClientKafka, Transport } from '@nestjs/microservices'; import { lastValueFrom, Observable } from 'rxjs'; -import { BusinessDto } from './dtos/business.dto'; -import { UserDto } from './dtos/user.dto'; +import { BusinessDto } from './dtos/business.dto.js'; +import { UserDto } from './dtos/user.dto.js'; @Controller() export class KafkaController implements OnModuleInit, OnModuleDestroy { diff --git a/integration/microservices/src/kafka/kafka.messages.controller.ts b/integration/microservices/src/kafka/kafka.messages.controller.ts index 29acae07f77..42e6424cd2e 100644 --- a/integration/microservices/src/kafka/kafka.messages.controller.ts +++ b/integration/microservices/src/kafka/kafka.messages.controller.ts @@ -1,10 +1,10 @@ import { Controller, Logger } from '@nestjs/common'; import { EventPattern, MessagePattern } from '@nestjs/microservices'; -import { BusinessDto } from './dtos/business.dto'; -import { UserDto } from './dtos/user.dto'; -import { BusinessEntity } from './entities/business.entity'; -import { UserEntity } from './entities/user.entity'; -import { KafkaController } from './kafka.controller'; +import { BusinessDto } from './dtos/business.dto.js'; +import { UserDto } from './dtos/user.dto.js'; +import { BusinessEntity } from './entities/business.entity.js'; +import { UserEntity } from './entities/user.entity.js'; +import { KafkaController } from './kafka.controller.js'; @Controller() export class KafkaMessagesController { diff --git a/integration/microservices/src/main.ts b/integration/microservices/src/main.ts index cdecc1bb686..dbca439014a 100644 --- a/integration/microservices/src/main.ts +++ b/integration/microservices/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { ApplicationModule } from './app.module'; +import { ApplicationModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(ApplicationModule); diff --git a/integration/microservices/src/nats/nats.controller.ts b/integration/microservices/src/nats/nats.controller.ts index ffcfb18687a..3920cad01f7 100644 --- a/integration/microservices/src/nats/nats.controller.ts +++ b/integration/microservices/src/nats/nats.controller.ts @@ -1,3 +1,4 @@ +import * as nats from '@nats-io/nats-core'; import { Body, Controller, Get, HttpCode, Post, Query } from '@nestjs/common'; import { ClientProxy, @@ -11,10 +12,9 @@ import { RpcException, Transport, } from '@nestjs/microservices'; -import * as nats from 'nats'; import { from, lastValueFrom, Observable, of, throwError } from 'rxjs'; import { catchError, scan } from 'rxjs/operators'; -import { NatsService } from './nats.service'; +import { NatsService } from './nats.service.js'; @Controller() export class NatsController { diff --git a/integration/microservices/src/tcp-tls/app.controller.ts b/integration/microservices/src/tcp-tls/app.controller.ts index 81101741fc0..516de7e1016 100644 --- a/integration/microservices/src/tcp-tls/app.controller.ts +++ b/integration/microservices/src/tcp-tls/app.controller.ts @@ -34,7 +34,10 @@ export class AppController { tlsOptions: { ca: [ fs - .readFileSync(path.join(__dirname, 'ca.cert.pem'), 'utf-8') + .readFileSync( + path.join(import.meta.dirname, 'ca.cert.pem'), + 'utf-8', + ) .toString(), ], }, diff --git a/integration/microservices/src/tcp-tls/app.module.ts b/integration/microservices/src/tcp-tls/app.module.ts index b3720d2a96d..919442c6c3e 100644 --- a/integration/microservices/src/tcp-tls/app.module.ts +++ b/integration/microservices/src/tcp-tls/app.module.ts @@ -7,12 +7,14 @@ import { RpcException, Transport, } from '@nestjs/microservices'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; import * as fs from 'fs'; import * as path from 'path'; -const caCert = fs.readFileSync(path.join(__dirname, 'ca.cert.pem')).toString(); +const caCert = fs + .readFileSync(path.join(import.meta.dirname, 'ca.cert.pem')) + .toString(); class ErrorHandlingProxy extends ClientTCP { constructor() { diff --git a/integration/microservices/tsconfig.json b/integration/microservices/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/microservices/tsconfig.json +++ b/integration/microservices/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/module-utils/src/integration.module-definition.ts b/integration/module-utils/src/integration.module-definition.ts index 89d7a984241..3814942c831 100644 --- a/integration/module-utils/src/integration.module-definition.ts +++ b/integration/module-utils/src/integration.module-definition.ts @@ -1,5 +1,5 @@ import { ConfigurableModuleBuilder } from '@nestjs/common'; -import { IntegrationModuleOptions } from './interfaces/integration-module-options.interface'; +import { IntegrationModuleOptions } from './interfaces/integration-module-options.interface.js'; export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = new ConfigurableModuleBuilder() diff --git a/integration/module-utils/src/integration.module.ts b/integration/module-utils/src/integration.module.ts index 3b994f4bd4f..44e08637c21 100644 --- a/integration/module-utils/src/integration.module.ts +++ b/integration/module-utils/src/integration.module.ts @@ -2,8 +2,8 @@ import { Inject, Module } from '@nestjs/common'; import { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, -} from './integration.module-definition'; -import { IntegrationModuleOptions } from './interfaces/integration-module-options.interface'; +} from './integration.module-definition.js'; +import { IntegrationModuleOptions } from './interfaces/integration-module-options.interface.js'; @Module({}) export class IntegrationModule extends ConfigurableModuleClass { diff --git a/integration/module-utils/test/integration-module.spec.ts b/integration/module-utils/test/integration-module.spec.ts index ea22a71c571..e4308c1de2e 100644 --- a/integration/module-utils/test/integration-module.spec.ts +++ b/integration/module-utils/test/integration-module.spec.ts @@ -1,6 +1,5 @@ import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { IntegrationModule } from '../src/integration.module'; +import { IntegrationModule } from '../src/integration.module.js'; describe('Module utils (ConfigurableModuleBuilder)', () => { it('should auto-generate "forRoot" method', async () => { @@ -16,7 +15,7 @@ describe('Module utils (ConfigurableModuleBuilder)', () => { const integrationModule = moduleRef.get(IntegrationModule); - expect(integrationModule.options).to.deep.equal({ + expect(integrationModule.options).toEqual({ url: 'test_url', secure: false, }); @@ -39,7 +38,7 @@ describe('Module utils (ConfigurableModuleBuilder)', () => { const integrationModule = moduleRef.get(IntegrationModule); - expect(integrationModule.options).to.deep.equal({ + expect(integrationModule.options).toEqual({ url: 'test_url', secure: false, }); diff --git a/integration/module-utils/tsconfig.json b/integration/module-utils/tsconfig.json index 2d3b89de293..34056a28cae 100644 --- a/integration/module-utils/tsconfig.json +++ b/integration/module-utils/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/mongoose/e2e/async-class-options.spec.ts b/integration/mongoose/e2e/async-class-options.spec.ts index 4a740279519..c97e57a7597 100644 --- a/integration/mongoose/e2e/async-class-options.spec.ts +++ b/integration/mongoose/e2e/async-class-options.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AsyncOptionsClassModule } from '../src/async-class-options.module'; +import request from 'supertest'; +import { AsyncOptionsClassModule } from '../src/async-class-options.module.js'; describe('Mongoose', () => { let server; diff --git a/integration/mongoose/e2e/async-existing-options.spec.ts b/integration/mongoose/e2e/async-existing-options.spec.ts index e507cb90aae..1e2fd7c624e 100644 --- a/integration/mongoose/e2e/async-existing-options.spec.ts +++ b/integration/mongoose/e2e/async-existing-options.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AsyncOptionsExistingModule } from '../src/async-existing-options.module'; +import request from 'supertest'; +import { AsyncOptionsExistingModule } from '../src/async-existing-options.module.js'; describe('Mongoose', () => { let server; diff --git a/integration/mongoose/e2e/async-options.spec.ts b/integration/mongoose/e2e/async-options.spec.ts index 738d5683fc1..b6bb0191171 100644 --- a/integration/mongoose/e2e/async-options.spec.ts +++ b/integration/mongoose/e2e/async-options.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AsyncOptionsFactoryModule } from '../src/async-options.module'; +import request from 'supertest'; +import { AsyncOptionsFactoryModule } from '../src/async-options.module.js'; describe('Mongoose', () => { let server; diff --git a/integration/mongoose/e2e/mongoose.spec.ts b/integration/mongoose/e2e/mongoose.spec.ts index 11fa260a9ac..607f09846ac 100644 --- a/integration/mongoose/e2e/mongoose.spec.ts +++ b/integration/mongoose/e2e/mongoose.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { ApplicationModule } from '../src/app.module'; +import request from 'supertest'; +import { ApplicationModule } from '../src/app.module.js'; describe('Mongoose', () => { let server; diff --git a/integration/mongoose/src/app.module.ts b/integration/mongoose/src/app.module.ts index 0368daa98a6..4d3fa0de320 100644 --- a/integration/mongoose/src/app.module.ts +++ b/integration/mongoose/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [ diff --git a/integration/mongoose/src/async-class-options.module.ts b/integration/mongoose/src/async-class-options.module.ts index 6e8a20e3f59..1b391de3451 100644 --- a/integration/mongoose/src/async-class-options.module.ts +++ b/integration/mongoose/src/async-class-options.module.ts @@ -4,7 +4,7 @@ import { MongooseModuleOptions, MongooseOptionsFactory, } from '@nestjs/mongoose'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; class ConfigService implements MongooseOptionsFactory { createMongooseOptions(): MongooseModuleOptions { diff --git a/integration/mongoose/src/async-existing-options.module.ts b/integration/mongoose/src/async-existing-options.module.ts index 61209d28aba..bf8c609e5b4 100644 --- a/integration/mongoose/src/async-existing-options.module.ts +++ b/integration/mongoose/src/async-existing-options.module.ts @@ -4,7 +4,7 @@ import { MongooseModuleOptions, MongooseOptionsFactory, } from '@nestjs/mongoose'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; class ConfigService implements MongooseOptionsFactory { createMongooseOptions(): MongooseModuleOptions { diff --git a/integration/mongoose/src/async-options.module.ts b/integration/mongoose/src/async-options.module.ts index d5f0f45d5f6..da9a808b5f4 100644 --- a/integration/mongoose/src/async-options.module.ts +++ b/integration/mongoose/src/async-options.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [ diff --git a/integration/mongoose/src/cats/cats.controller.ts b/integration/mongoose/src/cats/cats.controller.ts index 28c92f1853a..1dec9a5abc9 100644 --- a/integration/mongoose/src/cats/cats.controller.ts +++ b/integration/mongoose/src/cats/cats.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Get, Post } from '@nestjs/common'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './interfaces/cat.interface'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; @Controller('cats') export class CatsController { diff --git a/integration/mongoose/src/cats/cats.module.ts b/integration/mongoose/src/cats/cats.module.ts index dce6ab960c3..31b71de7f45 100644 --- a/integration/mongoose/src/cats/cats.module.ts +++ b/integration/mongoose/src/cats/cats.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; -import { CatSchema } from './schemas/cat.schema'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { CatSchema } from './schemas/cat.schema.js'; @Module({ imports: [MongooseModule.forFeature([{ name: 'Cat', schema: CatSchema }])], diff --git a/integration/mongoose/src/cats/cats.service.ts b/integration/mongoose/src/cats/cats.service.ts index 51d6e313a88..5731c1a9583 100644 --- a/integration/mongoose/src/cats/cats.service.ts +++ b/integration/mongoose/src/cats/cats.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './interfaces/cat.interface'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable() export class CatsService { diff --git a/integration/mongoose/src/cats/schemas/cat.schema.ts b/integration/mongoose/src/cats/schemas/cat.schema.ts index 56aaecae80a..2645f3ce46c 100644 --- a/integration/mongoose/src/cats/schemas/cat.schema.ts +++ b/integration/mongoose/src/cats/schemas/cat.schema.ts @@ -1,4 +1,4 @@ -import * as mongoose from 'mongoose'; +import mongoose from 'mongoose'; export const CatSchema = new mongoose.Schema({ name: String, diff --git a/integration/mongoose/src/main.ts b/integration/mongoose/src/main.ts index 2699ee9d7a2..855c45380c7 100644 --- a/integration/mongoose/src/main.ts +++ b/integration/mongoose/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { ApplicationModule } from './app.module'; +import { ApplicationModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(ApplicationModule); diff --git a/integration/mongoose/tsconfig.json b/integration/mongoose/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/mongoose/tsconfig.json +++ b/integration/mongoose/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/nest-application/app-locals/e2e/express.spec.ts b/integration/nest-application/app-locals/e2e/express.spec.ts index 1e8597851d8..f32fc2edb0d 100644 --- a/integration/nest-application/app-locals/e2e/express.spec.ts +++ b/integration/nest-application/app-locals/e2e/express.spec.ts @@ -1,8 +1,7 @@ import { NestExpressApplication } from '@nestjs/platform-express'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('App-level globals (Express Application)', () => { let moduleFixture: TestingModule; @@ -22,14 +21,14 @@ describe('App-level globals (Express Application)', () => { app.setLocal('title', 'My Website'); await app.init(); const response = await request(app.getHttpServer()).get('/').expect(200); - expect(response.body.title).to.equal('My Website'); + expect(response.body.title).toBe('My Website'); }); it('should get "email" from "app.locals"', async () => { app.setLocal('email', 'admin@example.com'); await app.listen(4444); const response = await request(app.getHttpServer()).get('/').expect(200); - expect(response.body.email).to.equal('admin@example.com'); + expect(response.body.email).toBe('admin@example.com'); }); afterEach(async () => { diff --git a/integration/nest-application/app-locals/src/app.module.ts b/integration/nest-application/app-locals/src/app.module.ts index 848d4aaa7fe..52bb569b5d1 100644 --- a/integration/nest-application/app-locals/src/app.module.ts +++ b/integration/nest-application/app-locals/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; @Module({ controllers: [AppController], diff --git a/integration/nest-application/app-locals/tsconfig.json b/integration/nest-application/app-locals/tsconfig.json index 259f2b525f8..76d55ca6982 100644 --- a/integration/nest-application/app-locals/tsconfig.json +++ b/integration/nest-application/app-locals/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "outDir": "./dist", diff --git a/integration/nest-application/get-url/e2e/express.spec.ts b/integration/nest-application/get-url/e2e/express.spec.ts index fc0cfad2b60..e32c19fba1a 100644 --- a/integration/nest-application/get-url/e2e/express.spec.ts +++ b/integration/nest-application/get-url/e2e/express.spec.ts @@ -1,9 +1,8 @@ import { ExpressAdapter } from '@nestjs/platform-express'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as express from 'express'; -import { AppModule } from '../src/app.module'; -import { randomPort } from './utils'; +import express from 'express'; +import { AppModule } from '../src/app.module.js'; +import { randomPort } from './utils.js'; describe('Get URL (Express Application)', () => { let testModule: TestingModule; @@ -19,39 +18,39 @@ describe('Get URL (Express Application)', () => { port = await randomPort(); }); - it('should be able to get the IPv6 address', async () => { + it('should be able to get a loopback address', async () => { const app = testModule.createNestApplication(new ExpressAdapter(express())); await app.listen(port); - expect(await app.getUrl()).to.be.eql(`http://[::1]:${port}`); + expect(await app.getUrl()).toMatch( + new RegExp(`http://(\\[::1\\]|127\\.0\\.0\\.1):${port}`), + ); await app.close(); }); it('should be able to get the IPv4 address', async () => { const app = testModule.createNestApplication(new ExpressAdapter(express())); - await app.listen(port, '127.0.0.5'); - expect(await app.getUrl()).to.be.eql(`http://127.0.0.5:${port}`); + await app.listen(port, '127.0.0.1'); + expect(await app.getUrl()).toEqual(`http://127.0.0.1:${port}`); await app.close(); }); it('should return 127.0.0.1 for 0.0.0.0', async () => { const app = testModule.createNestApplication(new ExpressAdapter(express())); await app.listen(port, '0.0.0.0'); - expect(await app.getUrl()).to.be.eql(`http://127.0.0.1:${port}`); + expect(await app.getUrl()).toEqual(`http://127.0.0.1:${port}`); await app.close(); }); - it('should return 127.0.0.1 even in a callback', () => { + it('should return a loopback address in a callback (default bind)', () => { const app = testModule.createNestApplication(new ExpressAdapter(express())); return app.listen(port, async () => { - expect(await app.getUrl()).to.be.eql(`http://127.0.0.1:${port}`); + expect(await app.getUrl()).toMatch( + new RegExp(`http://(\\[::1\\]|127\\.0\\.0\\.1):${port}`), + ); await app.close(); }); }); it('should throw an error for calling getUrl before listen', async () => { const app = testModule.createNestApplication(new ExpressAdapter(express())); - try { - await app.getUrl(); - } catch (err) { - expect(err).to.be.eql( - 'app.listen() needs to be called before calling app.getUrl()', - ); - } + await expect(app.getUrl()).rejects.toEqual( + 'app.listen() needs to be called before calling app.getUrl()', + ); }); }); diff --git a/integration/nest-application/get-url/e2e/fastify.spec.ts b/integration/nest-application/get-url/e2e/fastify.spec.ts index d1c8cfcaff0..ac75e5fd0c5 100644 --- a/integration/nest-application/get-url/e2e/fastify.spec.ts +++ b/integration/nest-application/get-url/e2e/fastify.spec.ts @@ -1,8 +1,7 @@ import { FastifyAdapter } from '@nestjs/platform-fastify'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import { AppModule } from '../src/app.module'; -import { randomPort } from './utils'; +import { AppModule } from '../src/app.module.js'; +import { randomPort } from './utils.js'; describe('Get URL (Fastify Application)', () => { let testModule: TestingModule; @@ -20,31 +19,29 @@ describe('Get URL (Fastify Application)', () => { it('should be able to get the IPv4 address', async () => { const app = testModule.createNestApplication(new FastifyAdapter()); - await app.listen(port, '127.0.0.5'); - expect(await app.getUrl()).to.be.eql(`http://127.0.0.5:${port}`); + await app.listen(port, '127.0.0.1'); + expect(await app.getUrl()).toEqual(`http://127.0.0.1:${port}`); await app.close(); }); it('should return 127.0.0.1 for 0.0.0.0', async () => { const app = testModule.createNestApplication(new FastifyAdapter()); await app.listen(port, '0.0.0.0'); - expect(await app.getUrl()).to.be.eql(`http://127.0.0.1:${port}`); + expect(await app.getUrl()).toEqual(`http://127.0.0.1:${port}`); await app.close(); }); - it('should return 127.0.0.1 even in a callback', () => { + it('should return a loopback address in a callback (default bind)', () => { const app = testModule.createNestApplication(new FastifyAdapter()); return app.listen(port, async () => { - expect(await app.getUrl()).to.be.eql(`http://127.0.0.1:${port}`); + expect(await app.getUrl()).toMatch( + new RegExp(`http://(\\[::1\\]|127\\.0\\.0\\.1):${port}`), + ); await app.close(); }); }); it('should throw an error for calling getUrl before listen', async () => { const app = testModule.createNestApplication(new FastifyAdapter()); - try { - await app.getUrl(); - } catch (err) { - expect(err).to.be.eql( - 'app.listen() needs to be called before calling app.getUrl()', - ); - } + await expect(app.getUrl()).rejects.toEqual( + 'app.listen() needs to be called before calling app.getUrl()', + ); }); }); diff --git a/integration/nest-application/get-url/src/app.controller.ts b/integration/nest-application/get-url/src/app.controller.ts index 8d4f6eb02ce..e1f9543ab91 100644 --- a/integration/nest-application/get-url/src/app.controller.ts +++ b/integration/nest-application/get-url/src/app.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; +import { AppService } from './app.service.js'; @Controller() export class AppController { diff --git a/integration/nest-application/get-url/src/app.module.ts b/integration/nest-application/get-url/src/app.module.ts index 7845d045f8d..7abec95862b 100644 --- a/integration/nest-application/get-url/src/app.module.ts +++ b/integration/nest-application/get-url/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { AppController } from './app.controller.js'; +import { AppService } from './app.service.js'; @Module({ controllers: [AppController], diff --git a/integration/nest-application/get-url/tsconfig.json b/integration/nest-application/get-url/tsconfig.json index 259f2b525f8..76d55ca6982 100644 --- a/integration/nest-application/get-url/tsconfig.json +++ b/integration/nest-application/get-url/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "outDir": "./dist", diff --git a/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts b/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts index 3364e29efa9..fc709c9721d 100644 --- a/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts +++ b/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts @@ -1,11 +1,11 @@ import { INestApplication, RequestMethod } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; +import request from 'supertest'; import { AppModule, MIDDLEWARE_PARAM_VALUE, MIDDLEWARE_VALUE, -} from '../src/app.module'; +} from '../src/app.module.js'; describe('Global prefix', () => { let server; diff --git a/integration/nest-application/global-prefix/src/app.module.ts b/integration/nest-application/global-prefix/src/app.module.ts index 61a1b0407fc..d8156b42ad8 100644 --- a/integration/nest-application/global-prefix/src/app.module.ts +++ b/integration/nest-application/global-prefix/src/app.module.ts @@ -1,5 +1,5 @@ import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; export const MIDDLEWARE_VALUE = 'middleware'; export const MIDDLEWARE_PARAM_VALUE = 'middleware_param'; diff --git a/integration/nest-application/global-prefix/tsconfig.json b/integration/nest-application/global-prefix/tsconfig.json index 259f2b525f8..76d55ca6982 100644 --- a/integration/nest-application/global-prefix/tsconfig.json +++ b/integration/nest-application/global-prefix/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "outDir": "./dist", diff --git a/integration/nest-application/listen/e2e/express.spec.ts b/integration/nest-application/listen/e2e/express.spec.ts index b209fa0c3f9..4494fa660dd 100644 --- a/integration/nest-application/listen/e2e/express.spec.ts +++ b/integration/nest-application/listen/e2e/express.spec.ts @@ -1,9 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { ExpressAdapter } from '@nestjs/platform-express'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as express from 'express'; -import { AppModule } from '../src/app.module'; +import express from 'express'; +import { AppModule } from '../src/app.module.js'; describe('Listen (Express Application)', () => { let testModule: TestingModule; @@ -22,7 +21,7 @@ describe('Listen (Express Application)', () => { it('should resolve with httpServer on success', async () => { const response = await app.listen(3000); - expect(response).to.eql(app.getHttpServer()); + expect(response).toEqual(app.getHttpServer()); }); it('should reject if the port is not available', async () => { @@ -30,18 +29,14 @@ describe('Listen (Express Application)', () => { const secondApp = testModule.createNestApplication( new ExpressAdapter(express()), ); - try { - await secondApp.listen(3000); - } catch (error) { - expect(error.code).to.equal('EADDRINUSE'); - } + await expect(secondApp.listen(3000)).rejects.toMatchObject({ + code: 'EADDRINUSE', + }); }); it('should reject if there is an invalid host', async () => { - try { - await app.listen(3000, '1'); - } catch (error) { - expect(error.code).to.equal('EADDRNOTAVAIL'); - } + await expect(app.listen(3000, '1')).rejects.toMatchObject({ + code: 'EADDRNOTAVAIL', + }); }); }); diff --git a/integration/nest-application/listen/e2e/fastify.spec.ts b/integration/nest-application/listen/e2e/fastify.spec.ts index ba267c8ddbb..42ab67a98ae 100644 --- a/integration/nest-application/listen/e2e/fastify.spec.ts +++ b/integration/nest-application/listen/e2e/fastify.spec.ts @@ -1,8 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { FastifyAdapter } from '@nestjs/platform-fastify'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; describe('Listen (Fastify Application)', () => { let testModule: TestingModule; @@ -21,26 +20,22 @@ describe('Listen (Fastify Application)', () => { it('should resolve with httpServer on success', async () => { const response = await app.listen(3000); - expect(response).to.eql(app.getHttpServer()); + expect(response).toEqual(app.getHttpServer()); }); it('should reject if the port is not available', async () => { await app.listen(3000); const secondApp = testModule.createNestApplication(new FastifyAdapter()); - try { - await secondApp.listen(3000); - } catch (error) { - expect(error.code).to.equal('EADDRINUSE'); - } + await expect(secondApp.listen(3000)).rejects.toMatchObject({ + code: 'EADDRINUSE', + }); await secondApp.close(); }); it('should reject if there is an invalid host', async () => { - try { - await app.listen(3000, '1'); - } catch (error) { - expect(error.code).to.equal('EADDRNOTAVAIL'); - } + await expect(app.listen(3000, '1')).rejects.toMatchObject({ + code: 'EADDRNOTAVAIL', + }); }); }); diff --git a/integration/nest-application/listen/src/app.controller.ts b/integration/nest-application/listen/src/app.controller.ts index 8d4f6eb02ce..e1f9543ab91 100644 --- a/integration/nest-application/listen/src/app.controller.ts +++ b/integration/nest-application/listen/src/app.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; +import { AppService } from './app.service.js'; @Controller() export class AppController { diff --git a/integration/nest-application/listen/src/app.module.ts b/integration/nest-application/listen/src/app.module.ts index 7845d045f8d..7abec95862b 100644 --- a/integration/nest-application/listen/src/app.module.ts +++ b/integration/nest-application/listen/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { AppController } from './app.controller.js'; +import { AppService } from './app.service.js'; @Module({ controllers: [AppController], diff --git a/integration/nest-application/listen/tsconfig.json b/integration/nest-application/listen/tsconfig.json index 259f2b525f8..76d55ca6982 100644 --- a/integration/nest-application/listen/tsconfig.json +++ b/integration/nest-application/listen/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "outDir": "./dist", diff --git a/integration/nest-application/raw-body/e2e/express.spec.ts b/integration/nest-application/raw-body/e2e/express.spec.ts index 762ecd92c65..2d70e2dc05a 100644 --- a/integration/nest-application/raw-body/e2e/express.spec.ts +++ b/integration/nest-application/raw-body/e2e/express.spec.ts @@ -1,8 +1,7 @@ import { NestExpressApplication } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { ExpressModule } from '../src/express.module'; +import request from 'supertest'; +import { ExpressModule } from '../src/express.module.js'; describe('Raw body (Express Application)', () => { let app: NestExpressApplication; @@ -33,7 +32,7 @@ describe('Raw body (Express Application)', () => { .send(body) .expect(201); - expect(response.body).to.eql({ + expect(response.body).toEqual({ parsed: { amount: 0, }, @@ -59,7 +58,7 @@ describe('Raw body (Express Application)', () => { .send(body) .expect(201); - expect(response.body).to.eql({ + expect(response.body).toEqual({ parsed: { content: 'this is a post\'s content by "Nest"', }, diff --git a/integration/nest-application/raw-body/e2e/fastify.spec.ts b/integration/nest-application/raw-body/e2e/fastify.spec.ts index 03db3f137d9..65bb8371e8a 100644 --- a/integration/nest-application/raw-body/e2e/fastify.spec.ts +++ b/integration/nest-application/raw-body/e2e/fastify.spec.ts @@ -3,8 +3,7 @@ import { FastifyAdapter, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { FastifyModule } from '../src/fastify.module'; +import { FastifyModule } from '../src/fastify.module.js'; describe('Raw body (Fastify Application)', () => { let app: NestFastifyApplication; @@ -39,7 +38,7 @@ describe('Raw body (Fastify Application)', () => { payload: body, }); - expect(JSON.parse(response.body)).to.eql({ + expect(JSON.parse(response.body)).toEqual({ parsed: { amount: 0, }, @@ -60,7 +59,7 @@ describe('Raw body (Fastify Application)', () => { // Unlike Express, when you send a POST request without a body // with Fastify, Fastify will throw an error because it isn't valid // JSON. See fastify/fastify#297. - expect(response.statusCode).to.equal(400); + expect(response.statusCode).toBe(400); }); }); @@ -75,7 +74,7 @@ describe('Raw body (Fastify Application)', () => { payload: body, }); - expect(JSON.parse(response.body)).to.eql({ + expect(JSON.parse(response.body)).toEqual({ parsed: { content: 'this is a post\'s content by "Nest"', }, @@ -92,7 +91,7 @@ describe('Raw body (Fastify Application)', () => { }, }); - expect(response.statusCode).to.equal(201); + expect(response.statusCode).toBe(201); }); }); }); diff --git a/integration/nest-application/raw-body/src/express.module.ts b/integration/nest-application/raw-body/src/express.module.ts index 5ddb714263c..4c08bd9aada 100644 --- a/integration/nest-application/raw-body/src/express.module.ts +++ b/integration/nest-application/raw-body/src/express.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { ExpressController } from './express.controller'; +import { ExpressController } from './express.controller.js'; @Module({ controllers: [ExpressController], diff --git a/integration/nest-application/raw-body/src/fastify.module.ts b/integration/nest-application/raw-body/src/fastify.module.ts index 462f427ba46..e803b8fb4a4 100644 --- a/integration/nest-application/raw-body/src/fastify.module.ts +++ b/integration/nest-application/raw-body/src/fastify.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { FastifyController } from './fastify.controller'; +import { FastifyController } from './fastify.controller.js'; @Module({ controllers: [FastifyController], diff --git a/integration/nest-application/raw-body/tsconfig.json b/integration/nest-application/raw-body/tsconfig.json index 259f2b525f8..76d55ca6982 100644 --- a/integration/nest-application/raw-body/tsconfig.json +++ b/integration/nest-application/raw-body/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "outDir": "./dist", diff --git a/integration/nest-application/sse/e2e/express.spec.ts b/integration/nest-application/sse/e2e/express.spec.ts index 483e1a75a60..d740ad8db24 100644 --- a/integration/nest-application/sse/e2e/express.spec.ts +++ b/integration/nest-application/sse/e2e/express.spec.ts @@ -1,8 +1,7 @@ import { NestExpressApplication } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { EventSource } from 'eventsource'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; describe('Sse (Express Application)', () => { let app: NestExpressApplication; @@ -40,14 +39,15 @@ describe('Sse (Express Application)', () => { await app.close(); }); - it('receives events from server', done => { - eventSource.addEventListener('message', event => { - expect(JSON.parse(event.data)).to.eql({ - hello: 'world', + it('receives events from server', () => + new Promise(done => { + eventSource.addEventListener('message', event => { + expect(JSON.parse(event.data)).toEqual({ + hello: 'world', + }); + done(); }); - done(); - }); - }); + })); }); describe('with forceCloseConnections', () => { @@ -81,13 +81,14 @@ describe('Sse (Express Application)', () => { eventSource.close(); }); - it('receives events from server', done => { - eventSource.addEventListener('message', event => { - expect(JSON.parse(event.data)).to.eql({ - hello: 'world', + it('receives events from server', () => + new Promise(done => { + eventSource.addEventListener('message', event => { + expect(JSON.parse(event.data)).toEqual({ + hello: 'world', + }); + done(); }); - done(); - }); - }); + })); }); }); diff --git a/integration/nest-application/sse/e2e/fastify.spec.ts b/integration/nest-application/sse/e2e/fastify.spec.ts index 98823c5f064..ad90b94b059 100644 --- a/integration/nest-application/sse/e2e/fastify.spec.ts +++ b/integration/nest-application/sse/e2e/fastify.spec.ts @@ -3,9 +3,8 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { EventSource } from 'eventsource'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; describe('Sse (Fastify Application)', () => { let app: NestFastifyApplication; @@ -45,14 +44,15 @@ describe('Sse (Fastify Application)', () => { await app.close(); }); - it('receives events from server', done => { - eventSource.addEventListener('message', event => { - expect(JSON.parse(event.data)).to.eql({ - hello: 'world', + it('receives events from server', () => + new Promise(done => { + eventSource.addEventListener('message', event => { + expect(JSON.parse(event.data)).toEqual({ + hello: 'world', + }); + done(); }); - done(); - }); - }); + })); }); describe('with forceCloseConnections', () => { @@ -88,13 +88,14 @@ describe('Sse (Fastify Application)', () => { eventSource.close(); }); - it('receives events from server', done => { - eventSource.addEventListener('message', event => { - expect(JSON.parse(event.data)).to.eql({ - hello: 'world', + it('receives events from server', () => + new Promise(done => { + eventSource.addEventListener('message', event => { + expect(JSON.parse(event.data)).toEqual({ + hello: 'world', + }); + done(); }); - done(); - }); - }); + })); }); }); diff --git a/integration/nest-application/sse/src/app.module.ts b/integration/nest-application/sse/src/app.module.ts index 848d4aaa7fe..52bb569b5d1 100644 --- a/integration/nest-application/sse/src/app.module.ts +++ b/integration/nest-application/sse/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; @Module({ controllers: [AppController], diff --git a/integration/nest-application/sse/tsconfig.json b/integration/nest-application/sse/tsconfig.json index 23ca656104e..16a4ee7e5c8 100644 --- a/integration/nest-application/sse/tsconfig.json +++ b/integration/nest-application/sse/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "lib": ["dom"], diff --git a/integration/nest-application/use-body-parser/e2e/express.spec.ts b/integration/nest-application/use-body-parser/e2e/express.spec.ts index 06e77086d7b..22c529f2738 100644 --- a/integration/nest-application/use-body-parser/e2e/express.spec.ts +++ b/integration/nest-application/use-body-parser/e2e/express.spec.ts @@ -1,9 +1,8 @@ import { NestExpressApplication } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; import { OptionsUrlencoded } from 'body-parser'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Body Parser (Express Application)', () => { const moduleFixture = Test.createTestingModule({ @@ -39,7 +38,7 @@ describe('Body Parser (Express Application)', () => { .send(stringLimit) .expect(201); - expect(response.body).to.eql({ + expect(response.body).toEqual({ raw: stringLimit, }); }); @@ -79,7 +78,7 @@ describe('Body Parser (Express Application)', () => { .send(stringLimit) .expect(201); - expect(response.body).to.eql({ + expect(response.body).toEqual({ raw: stringLimit, }); }); diff --git a/integration/nest-application/use-body-parser/e2e/fastify.spec.ts b/integration/nest-application/use-body-parser/e2e/fastify.spec.ts index 26a9240d2fd..2b02f8de251 100644 --- a/integration/nest-application/use-body-parser/e2e/fastify.spec.ts +++ b/integration/nest-application/use-body-parser/e2e/fastify.spec.ts @@ -3,8 +3,7 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; describe('Body Parser (Fastify Application)', () => { const moduleFixture = Test.createTestingModule({ @@ -43,7 +42,7 @@ describe('Body Parser (Fastify Application)', () => { payload: stringLimit, }); - expect(JSON.parse(response.body)).to.eql({ + expect(JSON.parse(response.body)).toEqual({ raw: stringLimit, }); }); @@ -56,7 +55,7 @@ describe('Body Parser (Fastify Application)', () => { payload: stringOverLimit, }); - expect(response.statusCode).to.equal(413); + expect(response.statusCode).toBe(413); }); }); @@ -87,7 +86,7 @@ describe('Body Parser (Fastify Application)', () => { payload: stringLimit, }); - expect(JSON.parse(response.body)).to.eql({ + expect(JSON.parse(response.body)).toEqual({ raw: stringLimit, }); }); @@ -100,7 +99,7 @@ describe('Body Parser (Fastify Application)', () => { payload: stringOverLimit, }); - expect(response.statusCode).to.equal(413); + expect(response.statusCode).toBe(413); }); }); }); diff --git a/integration/nest-application/use-body-parser/src/app.module.ts b/integration/nest-application/use-body-parser/src/app.module.ts index 848d4aaa7fe..52bb569b5d1 100644 --- a/integration/nest-application/use-body-parser/src/app.module.ts +++ b/integration/nest-application/use-body-parser/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; @Module({ controllers: [AppController], diff --git a/integration/nest-application/use-body-parser/tsconfig.json b/integration/nest-application/use-body-parser/tsconfig.json index 999ab15b777..d8f07b0aaa0 100644 --- a/integration/nest-application/use-body-parser/tsconfig.json +++ b/integration/nest-application/use-body-parser/tsconfig.json @@ -1,14 +1,16 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "lib": ["dom"], "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "outDir": "./dist" diff --git a/integration/repl/e2e/repl-process.spec.ts b/integration/repl/e2e/repl-process.spec.ts index 020439ef907..0a13457e242 100644 --- a/integration/repl/e2e/repl-process.spec.ts +++ b/integration/repl/e2e/repl-process.spec.ts @@ -1,9 +1,8 @@ -import { expect } from 'chai'; import { spawn } from 'child_process'; const PROMPT = '> '; -describe('REPL process', function () { +describe('REPL process', () => { let replProcess: ReturnType; function waitForReplToStart( @@ -35,24 +34,27 @@ describe('REPL process', function () { }); } - beforeEach(async function () { - this.timeout(15000); - replProcess = spawn('ts-node', ['../src/repl.ts'], { cwd: __dirname }); + beforeEach(async () => { + replProcess = spawn( + process.execPath, + ['--import', 'jiti/register', '../src/repl.ts'], + { + cwd: import.meta.dirname, + }, + ); await waitForReplToStart(replProcess, PROMPT); - }); + }, 15000); - afterEach(function () { + afterEach(() => { if (replProcess) { replProcess.kill(9); } }); - it('exits on .exit', async function () { - this.timeout(1000); - + it('exits on .exit', async () => { return new Promise((resolve, reject) => { replProcess.on('exit', _ => { - expect(replProcess.exitCode).to.equal(0); + expect(replProcess.exitCode).toBe(0); resolve(); }); @@ -66,5 +68,5 @@ describe('REPL process', function () { reject(new Error('REPL stdin is not available')); } }); - }); + }, 5000); }); diff --git a/integration/repl/e2e/repl.spec.ts b/integration/repl/e2e/repl.spec.ts index 8ddcc673d42..90d1266250b 100644 --- a/integration/repl/e2e/repl.spec.ts +++ b/integration/repl/e2e/repl.spec.ts @@ -1,4 +1,4 @@ -import { clc } from '@nestjs/common/utils/cli-colors.util'; +import { clc } from '@nestjs/common/utils/cli-colors.util.js'; import { repl } from '@nestjs/core'; import { DebugReplFn, @@ -8,38 +8,36 @@ import { ResolveReplFn, SelectReplFn, } from '@nestjs/core/repl/native-functions'; -import { ReplContext } from '@nestjs/core/repl/repl-context'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { AppModule } from '../src/app.module'; +import { ReplContext } from '@nestjs/core/repl/repl-context.js'; +import { AppModule } from '../src/app.module.js'; const PROMPT = '\u001b[1G\u001b[0J> \u001b[3G'; describe('REPL', () => { beforeEach(() => { // To avoid coloring the output: - sinon.stub(clc, 'bold').callsFake(text => text); - sinon.stub(clc, 'green').callsFake(text => text); - sinon.stub(clc, 'yellow').callsFake(text => text); - sinon.stub(clc, 'red').callsFake(text => text); - sinon.stub(clc, 'magentaBright').callsFake(text => text); - sinon.stub(clc, 'cyanBright').callsFake(text => text); + vi.spyOn(clc, 'bold').mockImplementation(text => text); + vi.spyOn(clc, 'green').mockImplementation(text => text); + vi.spyOn(clc, 'yellow').mockImplementation(text => text); + vi.spyOn(clc, 'red').mockImplementation(text => text); + vi.spyOn(clc, 'magentaBright').mockImplementation(text => text); + vi.spyOn(clc, 'cyanBright').mockImplementation(text => text); }); afterEach(() => { - sinon.restore(); + vi.restoreAllMocks(); }); it('get()', async () => { const server = await repl(AppModule); server.context; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); server.emit('line', 'get(UsersService)'); - expect(outputText).to.equal( + expect(outputText).toBe( `UsersService { usersRepository: UsersRepository {} } ${PROMPT}`, ); @@ -47,14 +45,13 @@ ${PROMPT}`, outputText = ''; server.emit('line', 'get(UsersService).findAll()'); - expect(outputText).to - .equal(`\u001b[32m'This action returns all users'\u001b[39m + expect(outputText).toBe(`\u001b[32m'This action returns all users'\u001b[39m ${PROMPT}`); outputText = ''; server.emit('line', 'get("UsersRepository")'); - expect(outputText).to.equal(`UsersRepository {} + expect(outputText).toBe(`UsersRepository {} ${PROMPT}`); }); @@ -62,13 +59,13 @@ ${PROMPT}`); const server = await repl(AppModule); server.context; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); server.emit('line', '$(UsersService)'); - expect(outputText).to.equal( + expect(outputText).toBe( `UsersService { usersRepository: UsersRepository {} } ${PROMPT}`, ); @@ -76,14 +73,13 @@ ${PROMPT}`, outputText = ''; server.emit('line', '$(UsersService).findAll()'); - expect(outputText).to - .equal(`\u001b[32m'This action returns all users'\u001b[39m + expect(outputText).toBe(`\u001b[32m'This action returns all users'\u001b[39m ${PROMPT}`); outputText = ''; server.emit('line', '$("UsersRepository")'); - expect(outputText).to.equal(`UsersRepository {} + expect(outputText).toBe(`UsersRepository {} ${PROMPT}`); }); @@ -91,13 +87,13 @@ ${PROMPT}`); const server = await repl(AppModule); let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); server.emit('line', 'debug(UsersModule)'); - expect(outputText).to.equal( + expect(outputText).toBe( ` UsersModule: - controllers: @@ -114,13 +110,13 @@ ${PROMPT}`, const server = await repl(AppModule); let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); server.emit('line', 'methods("UsersRepository")'); - expect(outputText).to.equal( + expect(outputText).toBe( ` Methods: ◻ find @@ -131,7 +127,7 @@ ${PROMPT}`, outputText = ''; server.emit('line', 'methods(UsersService)'); - expect(outputText).to.equal( + expect(outputText).toBe( ` Methods: ◻ create @@ -149,17 +145,17 @@ ${PROMPT}`, const replServer = await repl(AppModule); const { description, signature } = new HelpReplFn( - sinon.stub() as unknown as ReplContext, + vi.fn() as unknown as ReplContext, ).fnDefinition; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); replServer.emit('line', 'help.help'); - expect(outputText).to.equal(`${description} + expect(outputText).toBe(`${description} Interface: help${signature} ${PROMPT}`); }); @@ -168,17 +164,17 @@ ${PROMPT}`); const replServer = await repl(AppModule); const { description, signature } = new GetReplFn( - sinon.stub() as unknown as ReplContext, + vi.fn() as unknown as ReplContext, ).fnDefinition; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); replServer.emit('line', 'get.help'); - expect(outputText).to.equal(`${description} + expect(outputText).toBe(`${description} Interface: get${signature} ${PROMPT}`); }); @@ -187,17 +183,17 @@ ${PROMPT}`); const replServer = await repl(AppModule); const { description, signature } = new ResolveReplFn( - sinon.stub() as unknown as ReplContext, + vi.fn() as unknown as ReplContext, ).fnDefinition; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); replServer.emit('line', 'resolve.help'); - expect(outputText).to.equal(`${description} + expect(outputText).toBe(`${description} Interface: resolve${signature} ${PROMPT}`); }); @@ -206,17 +202,17 @@ ${PROMPT}`); const replServer = await repl(AppModule); const { description, signature } = new SelectReplFn( - sinon.stub() as unknown as ReplContext, + vi.fn() as unknown as ReplContext, ).fnDefinition; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); replServer.emit('line', 'select.help'); - expect(outputText).to.equal(`${description} + expect(outputText).toBe(`${description} Interface: select${signature} ${PROMPT}`); }); @@ -225,17 +221,17 @@ ${PROMPT}`); const replServer = await repl(AppModule); const { description, signature } = new DebugReplFn( - sinon.stub() as unknown as ReplContext, + vi.fn() as unknown as ReplContext, ).fnDefinition; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); replServer.emit('line', 'debug.help'); - expect(outputText).to.equal(`${description} + expect(outputText).toBe(`${description} Interface: debug${signature} ${PROMPT}`); }); @@ -244,17 +240,17 @@ ${PROMPT}`); const replServer = await repl(AppModule); const { description, signature } = new MethodsReplFn( - sinon.stub() as unknown as ReplContext, + vi.fn() as unknown as ReplContext, ).fnDefinition; let outputText = ''; - sinon.stub(process.stdout, 'write').callsFake(text => { + vi.spyOn(process.stdout, 'write').mockImplementation(text => { outputText += text as string; return true; }); replServer.emit('line', 'methods.help'); - expect(outputText).to.equal(`${description} + expect(outputText).toBe(`${description} Interface: methods${signature} ${PROMPT}`); }); diff --git a/integration/repl/src/app.module.ts b/integration/repl/src/app.module.ts index 867c4b1b3c5..7980cc54c1b 100644 --- a/integration/repl/src/app.module.ts +++ b/integration/repl/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { UsersModule } from './users/users.module'; +import { UsersModule } from './users/users.module.js'; @Module({ imports: [UsersModule], diff --git a/integration/repl/src/database/database.module.ts b/integration/repl/src/database/database.module.ts index 33d392d6a73..c0b6e23ac35 100644 --- a/integration/repl/src/database/database.module.ts +++ b/integration/repl/src/database/database.module.ts @@ -1,5 +1,5 @@ import { DynamicModule, Module } from '@nestjs/common'; -import { DatabaseConnection } from './database.connection'; +import { DatabaseConnection } from './database.connection.js'; @Module({}) export class DatabaseModule { diff --git a/integration/repl/src/long-living-app.module.ts b/integration/repl/src/long-living-app.module.ts index dd574a50628..05cef9d5273 100644 --- a/integration/repl/src/long-living-app.module.ts +++ b/integration/repl/src/long-living-app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { DatabaseModule } from './database/database.module'; +import { DatabaseModule } from './database/database.module.js'; @Module({ imports: [DatabaseModule.forRoot()], diff --git a/integration/repl/src/repl.ts b/integration/repl/src/repl.ts index b9b339517e9..5359a38c1a7 100644 --- a/integration/repl/src/repl.ts +++ b/integration/repl/src/repl.ts @@ -1,5 +1,5 @@ import { repl } from '@nestjs/core'; -import { LongLivingAppModule } from './long-living-app.module'; +import { LongLivingAppModule } from './long-living-app.module.js'; async function bootstrap() { await repl(LongLivingAppModule); diff --git a/integration/repl/src/users/dto/update-user.dto.ts b/integration/repl/src/users/dto/update-user.dto.ts index dfd37fb1edb..912cdc528b4 100644 --- a/integration/repl/src/users/dto/update-user.dto.ts +++ b/integration/repl/src/users/dto/update-user.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from '@nestjs/mapped-types'; -import { CreateUserDto } from './create-user.dto'; +import { CreateUserDto } from './create-user.dto.js'; export class UpdateUserDto extends PartialType(CreateUserDto) {} diff --git a/integration/repl/src/users/users.controller.ts b/integration/repl/src/users/users.controller.ts index d5db3a65e56..435e5438ee7 100644 --- a/integration/repl/src/users/users.controller.ts +++ b/integration/repl/src/users/users.controller.ts @@ -7,9 +7,9 @@ import { Param, Delete, } from '@nestjs/common'; -import { UsersService } from './users.service'; -import { CreateUserDto } from './dto/create-user.dto'; -import { UpdateUserDto } from './dto/update-user.dto'; +import { UsersService } from './users.service.js'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { UpdateUserDto } from './dto/update-user.dto.js'; @Controller('users') export class UsersController { diff --git a/integration/repl/src/users/users.module.ts b/integration/repl/src/users/users.module.ts index 86d73a3f0e4..57d0aeb82e8 100644 --- a/integration/repl/src/users/users.module.ts +++ b/integration/repl/src/users/users.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { UsersController } from './users.controller'; -import { UsersRepository } from './users.repository'; -import { UsersService } from './users.service'; +import { UsersController } from './users.controller.js'; +import { UsersRepository } from './users.repository.js'; +import { UsersService } from './users.service.js'; @Module({ controllers: [UsersController], diff --git a/integration/repl/src/users/users.service.ts b/integration/repl/src/users/users.service.ts index ab3191152d8..d2bfc6a7dd8 100644 --- a/integration/repl/src/users/users.service.ts +++ b/integration/repl/src/users/users.service.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { CreateUserDto } from './dto/create-user.dto'; -import { UpdateUserDto } from './dto/update-user.dto'; -import { UsersRepository } from './users.repository'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { UpdateUserDto } from './dto/update-user.dto.js'; +import { UsersRepository } from './users.repository.js'; @Injectable() export class UsersService { diff --git a/integration/repl/tsconfig.json b/integration/repl/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/repl/tsconfig.json +++ b/integration/repl/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/scopes/e2e/circular-request-scope.spec.ts b/integration/scopes/e2e/circular-request-scope.spec.ts index 72489dc60ec..130ae3b0049 100644 --- a/integration/scopes/e2e/circular-request-scope.spec.ts +++ b/integration/scopes/e2e/circular-request-scope.spec.ts @@ -1,11 +1,10 @@ import { INestApplication, Scope } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { HelloController } from '../src/circular-hello/hello.controller'; -import { HelloModule } from '../src/circular-hello/hello.module'; -import { HelloService } from '../src/circular-hello/hello.service'; -import { UsersService } from '../src/circular-hello/users/users.service'; +import request from 'supertest'; +import { HelloController } from '../src/circular-hello/hello.controller.js'; +import { HelloModule } from '../src/circular-hello/hello.module.js'; +import { HelloService } from '../src/circular-hello/hello.service.js'; +import { UsersService } from '../src/circular-hello/users/users.service.js'; class Meta { static COUNTER = 0; @@ -18,7 +17,7 @@ describe('Circular request scope', () => { let server; let app: INestApplication; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [ HelloModule.forRoot({ @@ -35,7 +34,7 @@ describe('Circular request scope', () => { }); describe('when one service is request scoped', () => { - before(async () => { + beforeAll(async () => { const performHttpCall = end => request(server) .get('/hello') @@ -49,23 +48,23 @@ describe('Circular request scope', () => { }); it(`should create controller for each request`, () => { - expect(HelloController.COUNTER).to.be.eql(3); + expect(HelloController.COUNTER).toEqual(3); }); it(`should create service for each request`, () => { - expect(UsersService.COUNTER).to.be.eql(3); + expect(UsersService.COUNTER).toEqual(3); }); it(`should create service for each request`, () => { - expect(HelloService.COUNTER).to.be.eql(3); + expect(HelloService.COUNTER).toEqual(3); }); it(`should create provider for each inquirer`, () => { - expect(Meta.COUNTER).to.be.eql(3); + expect(Meta.COUNTER).toEqual(3); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/scopes/e2e/circular-transient-scope.spec.ts b/integration/scopes/e2e/circular-transient-scope.spec.ts index acfaecbe4fe..209e62d25e6 100644 --- a/integration/scopes/e2e/circular-transient-scope.spec.ts +++ b/integration/scopes/e2e/circular-transient-scope.spec.ts @@ -1,11 +1,10 @@ import { INestApplication, Scope } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { HelloController } from '../src/circular-transient/hello.controller'; -import { HelloModule } from '../src/circular-transient/hello.module'; -import { HelloService } from '../src/circular-transient/hello.service'; -import { UsersService } from '../src/circular-transient/users/users.service'; +import request from 'supertest'; +import { HelloController } from '../src/circular-transient/hello.controller.js'; +import { HelloModule } from '../src/circular-transient/hello.module.js'; +import { HelloService } from '../src/circular-transient/hello.service.js'; +import { UsersService } from '../src/circular-transient/users/users.service.js'; class Meta { static COUNTER = 0; @@ -18,7 +17,7 @@ describe('Circular transient scope', () => { let server; let app: INestApplication; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [ HelloModule.forRoot({ @@ -35,7 +34,7 @@ describe('Circular transient scope', () => { }); describe('when one service is request scoped', () => { - before(async () => { + beforeAll(async () => { const performHttpCall = end => request(server) .get('/hello') @@ -49,23 +48,23 @@ describe('Circular transient scope', () => { }); it(`should create controller for each request`, async () => { - expect(HelloController.COUNTER).to.be.eql(3); + expect(HelloController.COUNTER).toEqual(3); }); it(`should create service for each request`, async () => { - expect(UsersService.COUNTER).to.be.eql(3); + expect(UsersService.COUNTER).toEqual(3); }); it(`should create service for each request`, async () => { - expect(HelloService.COUNTER).to.be.eql(3); + expect(HelloService.COUNTER).toEqual(3); }); it(`should create provider for each inquirer`, async () => { - expect(Meta.COUNTER).to.be.eql(7); + expect(Meta.COUNTER).toEqual(7); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/scopes/e2e/durable-providers.spec.ts b/integration/scopes/e2e/durable-providers.spec.ts index 39573b4e13b..ba912ed7681 100644 --- a/integration/scopes/e2e/durable-providers.spec.ts +++ b/integration/scopes/e2e/durable-providers.spec.ts @@ -1,16 +1,15 @@ import { INestApplication } from '@nestjs/common'; import { ContextIdFactory } from '@nestjs/core'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { DurableContextIdStrategy } from '../src/durable/durable-context-id.strategy'; -import { DurableModule } from '../src/durable/durable.module'; +import request from 'supertest'; +import { DurableContextIdStrategy } from '../src/durable/durable-context-id.strategy.js'; +import { DurableModule } from '../src/durable/durable.module.js'; describe('Durable providers', () => { let server: any; let app: INestApplication; - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [DurableModule], }).compile(); @@ -45,17 +44,17 @@ describe('Durable providers', () => { result = await new Promise(resolve => performHttpCall(1, resolve), ); - expect(result.text).equal('Hello world! Counter: 1'); + expect(result.text).toBe('Hello world! Counter: 1'); result = await new Promise(resolve => performHttpCall(1, resolve), ); - expect(result.text).equal('Hello world! Counter: 2'); + expect(result.text).toBe('Hello world! Counter: 2'); result = await new Promise(resolve => performHttpCall(1, resolve), ); - expect(result.text).equal('Hello world! Counter: 3'); + expect(result.text).toBe('Hello world! Counter: 3'); }); it(`should create per-tenant DI sub-tree`, async () => { @@ -63,17 +62,17 @@ describe('Durable providers', () => { result = await new Promise(resolve => performHttpCall(4, resolve), ); - expect(result.text).equal('Hello world! Counter: 1'); + expect(result.text).toBe('Hello world! Counter: 1'); result = await new Promise(resolve => performHttpCall(5, resolve), ); - expect(result.text).equal('Hello world! Counter: 1'); + expect(result.text).toBe('Hello world! Counter: 1'); result = await new Promise(resolve => performHttpCall(6, resolve), ); - expect(result.text).equal('Hello world! Counter: 1'); + expect(result.text).toBe('Hello world! Counter: 1'); }); it(`should register a custom per-tenant request payload`, async () => { @@ -81,12 +80,12 @@ describe('Durable providers', () => { result = await new Promise(resolve => performHttpCall(1, resolve, '/durable/echo'), ); - expect(result.body).deep.equal({ tenantId: '1' }); + expect(result.body).toEqual({ tenantId: '1' }); result = await new Promise(resolve => performHttpCall(3, resolve, '/durable/echo'), ); - expect(result.body).deep.equal({ tenantId: '3' }); + expect(result.body).toEqual({ tenantId: '3' }); }); it(`should return the same tenantId both from durable request scoped service and non-durable request scoped service`, async () => { @@ -94,7 +93,7 @@ describe('Durable providers', () => { result = await new Promise(resolve => performHttpCall(1, resolve, '/durable/request-context'), ); - expect(result.body).deep.equal({ + expect(result.body).toEqual({ durableService: '1', nonDurableService: '1', }); @@ -102,7 +101,7 @@ describe('Durable providers', () => { result = await new Promise(resolve => performHttpCall(2, resolve, '/durable/request-context'), ); - expect(result.body).deep.equal({ + expect(result.body).toEqual({ durableService: '2', nonDurableService: '2', }); @@ -115,18 +114,18 @@ describe('Durable providers', () => { performHttpCall(10, resolve, '/durable/echo', { forceError: true }), ); - expect(result.statusCode).equal(412); + expect(result.statusCode).toBe(412); // The second request should be successful result = await new Promise(resolve => performHttpCall(10, resolve, '/durable/echo'), ); - expect(result.body).deep.equal({ tenantId: '10' }); + expect(result.body).toEqual({ tenantId: '10' }); }); }); - after(async () => { + afterAll(async () => { ContextIdFactory['strategy'] = undefined; await app.close(); }); diff --git a/integration/scopes/e2e/inject-inquirer.spec.ts b/integration/scopes/e2e/inject-inquirer.spec.ts index f95d5ee0786..a1256ae0871 100644 --- a/integration/scopes/e2e/inject-inquirer.spec.ts +++ b/integration/scopes/e2e/inject-inquirer.spec.ts @@ -1,9 +1,7 @@ import { INestApplication, Logger } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import * as request from 'supertest'; -import { HelloModule } from '../src/inject-inquirer/hello.module'; +import request from 'supertest'; +import { HelloModule } from '../src/inject-inquirer/hello.module.js'; describe('Inject Inquirer', () => { let logger: Record; @@ -11,7 +9,7 @@ describe('Inject Inquirer', () => { let app: INestApplication; beforeEach(async () => { - logger = { log: sinon.spy() }; + logger = { log: vi.fn() }; const module = await Test.createTestingModule({ imports: [HelloModule], @@ -28,29 +26,28 @@ describe('Inject Inquirer', () => { it(`should allow the injection of inquirer in a Transient Scope`, async () => { await request(server).get('/hello/transient'); - expect( - logger.log.calledWith({ + expect(logger.log).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Hello transient!', feature: 'transient', }), - ).to.be.true; + ); }); it(`should allow the injection of the inquirer in a Request Scope`, async () => { await request(server).get('/hello/request'); - expect( - logger.log.calledWith({ + expect(logger.log).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Hello request!', - requestId: sinon.match.string, feature: 'request', }), - ).to.be.true; + ); - const requestId = logger.log.getCall(0).args[0].requestId; + const requestId = logger.log.mock.calls[0][0].requestId; - expect( - logger.log.calledWith({ + expect(logger.log).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Goodbye request!', requestId, feature: 'request', diff --git a/integration/scopes/e2e/msvc-request-scope.spec.ts b/integration/scopes/e2e/msvc-request-scope.spec.ts index 6ce1a6a5ace..446c83a15f3 100644 --- a/integration/scopes/e2e/msvc-request-scope.spec.ts +++ b/integration/scopes/e2e/msvc-request-scope.spec.ts @@ -1,13 +1,12 @@ import { INestApplication } from '@nestjs/common'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { Guard } from '../src/msvc/guards/request-scoped.guard'; -import { HelloController } from '../src/msvc/hello.controller'; -import { HelloModule } from '../src/msvc/hello.module'; -import { Interceptor } from '../src/msvc/interceptors/logging.interceptor'; -import { UsersService } from '../src/msvc/users/users.service'; +import request from 'supertest'; +import { Guard } from '../src/msvc/guards/request-scoped.guard.js'; +import { HelloController } from '../src/msvc/hello.controller.js'; +import { HelloModule } from '../src/msvc/hello.module.js'; +import { Interceptor } from '../src/msvc/interceptors/logging.interceptor.js'; +import { UsersService } from '../src/msvc/users/users.service.js'; class Meta { static COUNTER = 0; @@ -20,7 +19,7 @@ describe('Request scope (microservices)', () => { let server; let app: INestApplication; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [ HelloModule.forRoot({ @@ -39,7 +38,7 @@ describe('Request scope (microservices)', () => { }); describe('when one service is request scoped', () => { - before(async () => { + beforeAll(async () => { const performHttpCall = end => request(server) .get('/hello') @@ -53,29 +52,29 @@ describe('Request scope (microservices)', () => { }); it(`should create controller for each request`, () => { - expect(HelloController.COUNTER).to.be.eql(3); + expect(HelloController.COUNTER).toEqual(3); }); it(`should create service for each request`, () => { - expect(UsersService.COUNTER).to.be.eql(3); + expect(UsersService.COUNTER).toEqual(3); }); it(`should share static provider across requests`, () => { - expect(Meta.COUNTER).to.be.eql(1); + expect(Meta.COUNTER).toEqual(1); }); it(`should create request scoped interceptor for each request`, () => { - expect(Interceptor.COUNTER).to.be.eql(3); - expect(Interceptor.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]); + expect(Interceptor.COUNTER).toEqual(3); + expect(Interceptor.REQUEST_SCOPED_DATA).toEqual([1, 1, 1]); }); it(`should create request scoped guard for each request`, () => { - expect(Guard.COUNTER).to.be.eql(3); - expect(Guard.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]); + expect(Guard.COUNTER).toEqual(3); + expect(Guard.REQUEST_SCOPED_DATA).toEqual([1, 1, 1]); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/scopes/e2e/request-modules-scope.spec.ts b/integration/scopes/e2e/request-modules-scope.spec.ts index 0a7d54ada96..bb2faebe918 100644 --- a/integration/scopes/e2e/request-modules-scope.spec.ts +++ b/integration/scopes/e2e/request-modules-scope.spec.ts @@ -1,15 +1,14 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { RequestChainModule } from '../src/request-chain/request-chain.module'; -import { RequestChainService } from '../src/request-chain/request-chain.service'; +import request from 'supertest'; +import { RequestChainModule } from '../src/request-chain/request-chain.module.js'; +import { RequestChainService } from '../src/request-chain/request-chain.service.js'; describe('Request scope (modules propagation)', () => { let server; let app: INestApplication; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [RequestChainModule], }).compile(); @@ -20,7 +19,7 @@ describe('Request scope (modules propagation)', () => { }); describe('when service from parent module is request scoped', () => { - before(async () => { + beforeAll(async () => { const performHttpCall = end => request(server) .get('/hello') @@ -34,11 +33,11 @@ describe('Request scope (modules propagation)', () => { }); it(`should not fail`, () => { - expect(RequestChainService.COUNTER).to.be.eql(3); + expect(RequestChainService.COUNTER).toEqual(3); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/scopes/e2e/request-scope.spec.ts b/integration/scopes/e2e/request-scope.spec.ts index 7b146972b4f..e75627f9b84 100644 --- a/integration/scopes/e2e/request-scope.spec.ts +++ b/integration/scopes/e2e/request-scope.spec.ts @@ -1,13 +1,12 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { Guard } from '../src/hello/guards/request-scoped.guard'; -import { HelloController } from '../src/hello/hello.controller'; -import { HelloModule } from '../src/hello/hello.module'; -import { Interceptor } from '../src/hello/interceptors/logging.interceptor'; -import { UserByIdPipe } from '../src/hello/users/user-by-id.pipe'; -import { UsersService } from '../src/hello/users/users.service'; +import request from 'supertest'; +import { Guard } from '../src/hello/guards/request-scoped.guard.js'; +import { HelloController } from '../src/hello/hello.controller.js'; +import { HelloModule } from '../src/hello/hello.module.js'; +import { Interceptor } from '../src/hello/interceptors/logging.interceptor.js'; +import { UserByIdPipe } from '../src/hello/users/user-by-id.pipe.js'; +import { UsersService } from '../src/hello/users/users.service.js'; class Meta { static COUNTER = 0; @@ -20,7 +19,7 @@ describe('Request scope', () => { let server; let app: INestApplication; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [ HelloModule.forRoot({ @@ -36,7 +35,7 @@ describe('Request scope', () => { }); describe('when one service is request scoped', () => { - before(async () => { + beforeAll(async () => { const performHttpCall = end => request(server) .get('/hello') @@ -50,34 +49,34 @@ describe('Request scope', () => { }); it(`should create controller for each request`, () => { - expect(HelloController.COUNTER).to.be.eql(3); + expect(HelloController.COUNTER).toEqual(3); }); it(`should create service for each request`, () => { - expect(UsersService.COUNTER).to.be.eql(3); + expect(UsersService.COUNTER).toEqual(3); }); it(`should share static provider across requests`, () => { - expect(Meta.COUNTER).to.be.eql(1); + expect(Meta.COUNTER).toEqual(1); }); it(`should create request scoped pipe for each request`, () => { - expect(UserByIdPipe.COUNTER).to.be.eql(3); - expect(UserByIdPipe.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]); + expect(UserByIdPipe.COUNTER).toEqual(3); + expect(UserByIdPipe.REQUEST_SCOPED_DATA).toEqual([1, 1, 1]); }); it(`should create request scoped interceptor for each request`, () => { - expect(Interceptor.COUNTER).to.be.eql(3); - expect(Interceptor.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]); + expect(Interceptor.COUNTER).toEqual(3); + expect(Interceptor.REQUEST_SCOPED_DATA).toEqual([1, 1, 1]); }); it(`should create request scoped guard for each request`, () => { - expect(Guard.COUNTER).to.be.eql(3); - expect(Guard.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]); + expect(Guard.COUNTER).toEqual(3); + expect(Guard.REQUEST_SCOPED_DATA).toEqual([1, 1, 1]); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/scopes/e2e/resolve-scoped.spec.ts b/integration/scopes/e2e/resolve-scoped.spec.ts index 138c8fe853a..4b7da02973d 100644 --- a/integration/scopes/e2e/resolve-scoped.spec.ts +++ b/integration/scopes/e2e/resolve-scoped.spec.ts @@ -1,9 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import { loggerProvider } from '../src/resolve-scoped/logger.provider'; -import { LoggerService } from '../src/resolve-scoped/logger.service'; -import { RequestLoggerService } from '../src/resolve-scoped/request-logger.service'; +import { loggerProvider } from '../src/resolve-scoped/logger.provider.js'; +import { LoggerService } from '../src/resolve-scoped/logger.service.js'; +import { RequestLoggerService } from '../src/resolve-scoped/request-logger.service.js'; describe('Resolve method', () => { let app: INestApplication; @@ -19,7 +18,7 @@ describe('Resolve method', () => { it('should resolve transient logger', async () => { const transientLogger = await app.resolve(LoggerService); - expect(transientLogger.logger).to.be.eql({ + expect(transientLogger.logger).toEqual({ logger: true, }); }); @@ -27,13 +26,13 @@ describe('Resolve method', () => { it('should resolve request-scoped logger', async () => { const requestScoped = await app.resolve(RequestLoggerService); - expect(requestScoped.loggerService).to.be.instanceOf(LoggerService); - expect(requestScoped.loggerService.logger).to.be.eql({ + expect(requestScoped.loggerService).toBeInstanceOf(LoggerService); + expect(requestScoped.loggerService.logger).toEqual({ logger: true, }); }); - after(async () => { + afterEach(async () => { await app.close(); }); }); diff --git a/integration/scopes/e2e/transient-scope.spec.ts b/integration/scopes/e2e/transient-scope.spec.ts index ce6a403cb57..c6f777186d2 100644 --- a/integration/scopes/e2e/transient-scope.spec.ts +++ b/integration/scopes/e2e/transient-scope.spec.ts @@ -1,14 +1,13 @@ import { INestApplication, Injectable, Scope } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as request from 'supertest'; -import { NestedTransientModule } from '../src/nested-transient/nested-transient.module'; -import { Guard } from '../src/transient/guards/request-scoped.guard'; -import { HelloController } from '../src/transient/hello.controller'; -import { HelloModule } from '../src/transient/hello.module'; -import { Interceptor } from '../src/transient/interceptors/logging.interceptor'; -import { UserByIdPipe } from '../src/transient/users/user-by-id.pipe'; -import { UsersService } from '../src/transient/users/users.service'; +import request from 'supertest'; +import { NestedTransientModule } from '../src/nested-transient/nested-transient.module.js'; +import { Guard } from '../src/transient/guards/request-scoped.guard.js'; +import { HelloController } from '../src/transient/hello.controller.js'; +import { HelloModule } from '../src/transient/hello.module.js'; +import { Interceptor } from '../src/transient/interceptors/logging.interceptor.js'; +import { UserByIdPipe } from '../src/transient/users/user-by-id.pipe.js'; +import { UsersService } from '../src/transient/users/users.service.js'; class Meta { static COUNTER = 0; @@ -22,7 +21,7 @@ describe('Transient scope', () => { let server: any; let app: INestApplication; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [ HelloModule.forRoot({ @@ -39,7 +38,7 @@ describe('Transient scope', () => { }); describe('and when one service is request scoped', () => { - before(async () => { + beforeAll(async () => { const performHttpCall = end => request(server) .get('/hello') @@ -53,31 +52,31 @@ describe('Transient scope', () => { }); it(`should create controller for each request`, () => { - expect(HelloController.COUNTER).to.be.eql(3); + expect(HelloController.COUNTER).toEqual(3); }); it(`should create service for each request`, () => { - expect(UsersService.COUNTER).to.be.eql(3); + expect(UsersService.COUNTER).toEqual(3); }); it(`should create provider for each inquirer`, () => { - expect(Meta.COUNTER).to.be.eql(7); + expect(Meta.COUNTER).toEqual(7); }); it(`should create transient pipe for each controller (3 requests, 1 static)`, () => { - expect(UserByIdPipe.COUNTER).to.be.eql(4); + expect(UserByIdPipe.COUNTER).toEqual(4); }); it(`should create transient interceptor for each controller (3 requests, 1 static)`, () => { - expect(Interceptor.COUNTER).to.be.eql(4); + expect(Interceptor.COUNTER).toEqual(4); }); it(`should create transient guard for each controller (3 requests, 1 static)`, () => { - expect(Guard.COUNTER).to.be.eql(4); + expect(Guard.COUNTER).toEqual(4); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); @@ -117,7 +116,7 @@ describe('Transient scope', () => { } } - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ providers: [FirstService, SecondService, LoggerService, DeepTransient], }).compile(); @@ -129,14 +128,14 @@ describe('Transient scope', () => { it('should create a new instance of the transient provider for each provider', async () => { const firstService1 = app.get(FirstService); - expect(firstService1.secondService.loggerService.context).to.equal( + expect(firstService1.secondService.loggerService.context).toBe( 'SecondService', ); - expect(firstService1.loggerService.context).to.equal('FirstService'); - expect(firstService1.deepTransient.initialized).to.be.true; + expect(firstService1.loggerService.context).toBe('FirstService'); + expect(firstService1.deepTransient.initialized).toBe(true); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); @@ -163,7 +162,7 @@ describe('Transient scope', () => { constructor(public readonly middle: MiddleTransient) {} } - before(async () => { + beforeAll(async () => { DeepNestedTransient.constructorCalled = false; const module = await Test.createTestingModule({ @@ -177,11 +176,11 @@ describe('Transient scope', () => { it('should call constructor of deeply nested TRANSIENT provider', () => { const rootService = app.get(RootService); - expect(DeepNestedTransient.constructorCalled).to.be.true; - expect(rootService.middle.nested).to.be.instanceOf(DeepNestedTransient); + expect(DeepNestedTransient.constructorCalled).toBe(true); + expect(rootService.middle.nested).toBeInstanceOf(DeepNestedTransient); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); @@ -190,7 +189,7 @@ describe('Transient scope', () => { let server: any; let app: INestApplication; - before(async () => { + beforeAll(async () => { const module = await Test.createTestingModule({ imports: [NestedTransientModule], }).compile(); @@ -203,7 +202,7 @@ describe('Transient scope', () => { describe('when handling HTTP requests', () => { let response: any; - before(async () => { + beforeAll(async () => { const performHttpCall = () => new Promise((resolve, reject) => { request(server) @@ -218,19 +217,15 @@ describe('Transient scope', () => { }); it('should isolate nested transient instances for each parent service', () => { - expect(response.body.firstServiceContext).to.equal( - 'NESTED-FirstService', - ); - expect(response.body.secondServiceContext).to.equal( - 'NESTED-SecondService', - ); - expect(response.body.firstServiceNestedId).to.not.equal( + expect(response.body.firstServiceContext).toBe('NESTED-FirstService'); + expect(response.body.secondServiceContext).toBe('NESTED-SecondService'); + expect(response.body.firstServiceNestedId).not.toBe( response.body.secondServiceNestedId, ); }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/scopes/src/app.module.ts b/integration/scopes/src/app.module.ts index 02a11788bdb..48bdbf4a6db 100644 --- a/integration/scopes/src/app.module.ts +++ b/integration/scopes/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { HelloModule } from './hello/hello.module'; +import { HelloModule } from './hello/hello.module.js'; @Module({ imports: [HelloModule.forRoot({ provide: 'META', useValue: true })], diff --git a/integration/scopes/src/circular-hello/hello.controller.ts b/integration/scopes/src/circular-hello/hello.controller.ts index 5b4af7bf215..94d20f26cf3 100644 --- a/integration/scopes/src/circular-hello/hello.controller.ts +++ b/integration/scopes/src/circular-hello/hello.controller.ts @@ -5,11 +5,11 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { Guard } from './guards/request-scoped.guard'; -import { HelloService } from './hello.service'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UserByIdPipe } from './users/user-by-id.pipe'; -import { UsersService } from './users/users.service'; +import { Guard } from './guards/request-scoped.guard.js'; +import { HelloService } from './hello.service.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; +import { UsersService } from './users/users.service.js'; @Controller('hello') export class HelloController { diff --git a/integration/scopes/src/circular-hello/hello.module.ts b/integration/scopes/src/circular-hello/hello.module.ts index 1a0d0bbfd7c..c0dc1995c66 100644 --- a/integration/scopes/src/circular-hello/hello.module.ts +++ b/integration/scopes/src/circular-hello/hello.module.ts @@ -1,7 +1,7 @@ import { DynamicModule, Inject, Module, Provider } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloService } from './hello.service'; -import { UsersService } from './users/users.service'; +import { HelloController } from './hello.controller.js'; +import { HelloService } from './hello.service.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HelloController], diff --git a/integration/scopes/src/circular-hello/users/user-by-id.pipe.ts b/integration/scopes/src/circular-hello/users/user-by-id.pipe.ts index 2b81cb6fbb5..b63f924559f 100644 --- a/integration/scopes/src/circular-hello/users/user-by-id.pipe.ts +++ b/integration/scopes/src/circular-hello/users/user-by-id.pipe.ts @@ -1,5 +1,5 @@ import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Injectable() export class UserByIdPipe implements PipeTransform { diff --git a/integration/scopes/src/circular-transient/hello.controller.ts b/integration/scopes/src/circular-transient/hello.controller.ts index 5b4af7bf215..94d20f26cf3 100644 --- a/integration/scopes/src/circular-transient/hello.controller.ts +++ b/integration/scopes/src/circular-transient/hello.controller.ts @@ -5,11 +5,11 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { Guard } from './guards/request-scoped.guard'; -import { HelloService } from './hello.service'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UserByIdPipe } from './users/user-by-id.pipe'; -import { UsersService } from './users/users.service'; +import { Guard } from './guards/request-scoped.guard.js'; +import { HelloService } from './hello.service.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; +import { UsersService } from './users/users.service.js'; @Controller('hello') export class HelloController { diff --git a/integration/scopes/src/circular-transient/hello.module.ts b/integration/scopes/src/circular-transient/hello.module.ts index 6cdc92c5f27..db10f010597 100644 --- a/integration/scopes/src/circular-transient/hello.module.ts +++ b/integration/scopes/src/circular-transient/hello.module.ts @@ -1,8 +1,8 @@ import { DynamicModule, Inject, Module, Provider } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloService } from './hello.service'; -import { TestController } from './test.controller'; -import { UsersService } from './users/users.service'; +import { HelloController } from './hello.controller.js'; +import { HelloService } from './hello.service.js'; +import { TestController } from './test.controller.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HelloController, TestController], diff --git a/integration/scopes/src/circular-transient/test.controller.ts b/integration/scopes/src/circular-transient/test.controller.ts index 572ea13a90f..ac1002a7898 100644 --- a/integration/scopes/src/circular-transient/test.controller.ts +++ b/integration/scopes/src/circular-transient/test.controller.ts @@ -5,9 +5,9 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { Guard } from './guards/request-scoped.guard'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UserByIdPipe } from './users/user-by-id.pipe'; +import { Guard } from './guards/request-scoped.guard.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; @Controller('test') export class TestController { diff --git a/integration/scopes/src/durable/durable.controller.ts b/integration/scopes/src/durable/durable.controller.ts index fbc33c12c98..47e88f19ef7 100644 --- a/integration/scopes/src/durable/durable.controller.ts +++ b/integration/scopes/src/durable/durable.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get } from '@nestjs/common'; -import { DurableService } from './durable.service'; -import { NonDurableService } from './non-durable.service'; +import { DurableService } from './durable.service.js'; +import { NonDurableService } from './non-durable.service.js'; @Controller('durable') export class DurableController { diff --git a/integration/scopes/src/durable/durable.guard.ts b/integration/scopes/src/durable/durable.guard.ts index 8868af8096a..9d0923f3863 100644 --- a/integration/scopes/src/durable/durable.guard.ts +++ b/integration/scopes/src/durable/durable.guard.ts @@ -4,7 +4,7 @@ import { Injectable, Scope, } from '@nestjs/common'; -import { DurableService } from './durable.service'; +import { DurableService } from './durable.service.js'; @Injectable({ scope: Scope.REQUEST, durable: true }) export class DurableGuard implements CanActivate { diff --git a/integration/scopes/src/durable/durable.module.ts b/integration/scopes/src/durable/durable.module.ts index dc91cded632..6eb7d1831bf 100644 --- a/integration/scopes/src/durable/durable.module.ts +++ b/integration/scopes/src/durable/durable.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; -import { DurableController } from './durable.controller'; -import { DurableGuard } from './durable.guard'; -import { DurableService } from './durable.service'; -import { NonDurableService } from './non-durable.service'; +import { DurableController } from './durable.controller.js'; +import { DurableGuard } from './durable.guard.js'; +import { DurableService } from './durable.service.js'; +import { NonDurableService } from './non-durable.service.js'; @Module({ controllers: [DurableController], diff --git a/integration/scopes/src/durable/durable.service.ts b/integration/scopes/src/durable/durable.service.ts index 15fcafe9663..102078df5fd 100644 --- a/integration/scopes/src/durable/durable.service.ts +++ b/integration/scopes/src/durable/durable.service.ts @@ -5,7 +5,7 @@ import { Scope, } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; -import { TenantContext } from './durable-context-id.strategy'; +import { TenantContext } from './durable-context-id.strategy.js'; @Injectable({ scope: Scope.REQUEST, durable: true }) export class DurableService { diff --git a/integration/scopes/src/durable/non-durable.service.ts b/integration/scopes/src/durable/non-durable.service.ts index add0629411b..203bc76b941 100644 --- a/integration/scopes/src/durable/non-durable.service.ts +++ b/integration/scopes/src/durable/non-durable.service.ts @@ -1,6 +1,6 @@ import { Inject, Injectable, Scope } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; -import { TenantContext } from './durable-context-id.strategy'; +import { TenantContext } from './durable-context-id.strategy.js'; @Injectable() export class NonDurableService { diff --git a/integration/scopes/src/hello/hello.controller.ts b/integration/scopes/src/hello/hello.controller.ts index 5b4af7bf215..94d20f26cf3 100644 --- a/integration/scopes/src/hello/hello.controller.ts +++ b/integration/scopes/src/hello/hello.controller.ts @@ -5,11 +5,11 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { Guard } from './guards/request-scoped.guard'; -import { HelloService } from './hello.service'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UserByIdPipe } from './users/user-by-id.pipe'; -import { UsersService } from './users/users.service'; +import { Guard } from './guards/request-scoped.guard.js'; +import { HelloService } from './hello.service.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; +import { UsersService } from './users/users.service.js'; @Controller('hello') export class HelloController { diff --git a/integration/scopes/src/hello/hello.module.ts b/integration/scopes/src/hello/hello.module.ts index 8bc5b72bcd6..cfdfbe1bec5 100644 --- a/integration/scopes/src/hello/hello.module.ts +++ b/integration/scopes/src/hello/hello.module.ts @@ -1,7 +1,7 @@ import { DynamicModule, Inject, Module, Provider, Scope } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloService } from './hello.service'; -import { UsersService } from './users/users.service'; +import { HelloController } from './hello.controller.js'; +import { HelloService } from './hello.service.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HelloController], diff --git a/integration/scopes/src/hello/users/user-by-id.pipe.ts b/integration/scopes/src/hello/users/user-by-id.pipe.ts index 73d859bfaf9..d4960af8d1a 100644 --- a/integration/scopes/src/hello/users/user-by-id.pipe.ts +++ b/integration/scopes/src/hello/users/user-by-id.pipe.ts @@ -4,7 +4,7 @@ import { Injectable, PipeTransform, } from '@nestjs/common'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Injectable() export class UserByIdPipe implements PipeTransform { diff --git a/integration/scopes/src/inject-inquirer/hello-request/hello-request.service.ts b/integration/scopes/src/inject-inquirer/hello-request/hello-request.service.ts index 4d63696b219..55a296162bb 100644 --- a/integration/scopes/src/inject-inquirer/hello-request/hello-request.service.ts +++ b/integration/scopes/src/inject-inquirer/hello-request/hello-request.service.ts @@ -1,6 +1,6 @@ import { Inject, Injectable, Scope } from '@nestjs/common'; import { INQUIRER } from '@nestjs/core'; -import { RequestLogger } from './request-logger.service'; +import { RequestLogger } from './request-logger.service.js'; @Injectable({ scope: Scope.REQUEST }) export class HelloRequestService { diff --git a/integration/scopes/src/inject-inquirer/hello-transient/hello-transient.service.ts b/integration/scopes/src/inject-inquirer/hello-transient/hello-transient.service.ts index 257544361f3..704cfcc6d88 100644 --- a/integration/scopes/src/inject-inquirer/hello-transient/hello-transient.service.ts +++ b/integration/scopes/src/inject-inquirer/hello-transient/hello-transient.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { TransientLogger } from './transient-logger.service'; +import { TransientLogger } from './transient-logger.service.js'; @Injectable() export class HelloTransientService { diff --git a/integration/scopes/src/inject-inquirer/hello.controller.ts b/integration/scopes/src/inject-inquirer/hello.controller.ts index b74a9508512..ce6216b1dcf 100644 --- a/integration/scopes/src/inject-inquirer/hello.controller.ts +++ b/integration/scopes/src/inject-inquirer/hello.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Scope } from '@nestjs/common'; -import { HelloRequestService } from './hello-request/hello-request.service'; -import { HelloTransientService } from './hello-transient/hello-transient.service'; +import { HelloRequestService } from './hello-request/hello-request.service.js'; +import { HelloTransientService } from './hello-transient/hello-transient.service.js'; @Controller({ path: 'hello', diff --git a/integration/scopes/src/inject-inquirer/hello.module.ts b/integration/scopes/src/inject-inquirer/hello.module.ts index fc7b640e2b8..ecd4f3a5727 100644 --- a/integration/scopes/src/inject-inquirer/hello.module.ts +++ b/integration/scopes/src/inject-inquirer/hello.module.ts @@ -1,9 +1,9 @@ import { Module, Logger } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloRequestService } from './hello-request/hello-request.service'; -import { HelloTransientService } from './hello-transient/hello-transient.service'; -import { RequestLogger } from './hello-request/request-logger.service'; -import { TransientLogger } from './hello-transient/transient-logger.service'; +import { HelloController } from './hello.controller.js'; +import { HelloRequestService } from './hello-request/hello-request.service.js'; +import { HelloTransientService } from './hello-transient/hello-transient.service.js'; +import { RequestLogger } from './hello-request/request-logger.service.js'; +import { TransientLogger } from './hello-transient/transient-logger.service.js'; @Module({ controllers: [HelloController], diff --git a/integration/scopes/src/main.ts b/integration/scopes/src/main.ts index cdecc1bb686..dbca439014a 100644 --- a/integration/scopes/src/main.ts +++ b/integration/scopes/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { ApplicationModule } from './app.module'; +import { ApplicationModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(ApplicationModule); diff --git a/integration/scopes/src/msvc/hello.controller.ts b/integration/scopes/src/msvc/hello.controller.ts index 0db4d6f6cad..39266618491 100644 --- a/integration/scopes/src/msvc/hello.controller.ts +++ b/integration/scopes/src/msvc/hello.controller.ts @@ -1,9 +1,9 @@ import { Controller, UseGuards, UseInterceptors } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; -import { Guard } from './guards/request-scoped.guard'; -import { HelloService } from './hello.service'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UsersService } from './users/users.service'; +import { Guard } from './guards/request-scoped.guard.js'; +import { HelloService } from './hello.service.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UsersService } from './users/users.service.js'; @Controller() export class HelloController { diff --git a/integration/scopes/src/msvc/hello.module.ts b/integration/scopes/src/msvc/hello.module.ts index 41d80066ab7..3ee48e7ca9f 100644 --- a/integration/scopes/src/msvc/hello.module.ts +++ b/integration/scopes/src/msvc/hello.module.ts @@ -1,8 +1,8 @@ import { DynamicModule, Inject, Module, Provider, Scope } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloService } from './hello.service'; -import { HttpController } from './http.controller'; -import { UsersService } from './users/users.service'; +import { HelloController } from './hello.controller.js'; +import { HelloService } from './hello.service.js'; +import { HttpController } from './http.controller.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HelloController, HttpController], diff --git a/integration/scopes/src/nested-transient/first-request.service.ts b/integration/scopes/src/nested-transient/first-request.service.ts index 9b1a349bac5..9c0ac5f5cb8 100644 --- a/integration/scopes/src/nested-transient/first-request.service.ts +++ b/integration/scopes/src/nested-transient/first-request.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { TransientLoggerService } from './transient-logger.service'; +import { TransientLoggerService } from './transient-logger.service.js'; @Injectable({ scope: Scope.REQUEST }) export class FirstRequestService { diff --git a/integration/scopes/src/nested-transient/nested-transient.controller.ts b/integration/scopes/src/nested-transient/nested-transient.controller.ts index 6ae21d297cc..95dbc5d39c7 100644 --- a/integration/scopes/src/nested-transient/nested-transient.controller.ts +++ b/integration/scopes/src/nested-transient/nested-transient.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Scope } from '@nestjs/common'; -import { FirstRequestService } from './first-request.service'; -import { SecondRequestService } from './second-request.service'; +import { FirstRequestService } from './first-request.service.js'; +import { SecondRequestService } from './second-request.service.js'; @Controller({ path: 'nested-transient', scope: Scope.REQUEST }) export class NestedTransientController { diff --git a/integration/scopes/src/nested-transient/nested-transient.module.ts b/integration/scopes/src/nested-transient/nested-transient.module.ts index 65024005864..8363a706570 100644 --- a/integration/scopes/src/nested-transient/nested-transient.module.ts +++ b/integration/scopes/src/nested-transient/nested-transient.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; -import { NestedTransientController } from './nested-transient.controller'; -import { FirstRequestService } from './first-request.service'; -import { SecondRequestService } from './second-request.service'; -import { TransientLoggerService } from './transient-logger.service'; -import { NestedTransientService } from './nested-transient.service'; +import { NestedTransientController } from './nested-transient.controller.js'; +import { FirstRequestService } from './first-request.service.js'; +import { SecondRequestService } from './second-request.service.js'; +import { TransientLoggerService } from './transient-logger.service.js'; +import { NestedTransientService } from './nested-transient.service.js'; @Module({ controllers: [NestedTransientController], diff --git a/integration/scopes/src/nested-transient/second-request.service.ts b/integration/scopes/src/nested-transient/second-request.service.ts index 0406877c7a6..dcae4c8ac0b 100644 --- a/integration/scopes/src/nested-transient/second-request.service.ts +++ b/integration/scopes/src/nested-transient/second-request.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { TransientLoggerService } from './transient-logger.service'; +import { TransientLoggerService } from './transient-logger.service.js'; @Injectable({ scope: Scope.REQUEST }) export class SecondRequestService { diff --git a/integration/scopes/src/nested-transient/transient-logger.service.ts b/integration/scopes/src/nested-transient/transient-logger.service.ts index cb25159fd5a..716b1856db1 100644 --- a/integration/scopes/src/nested-transient/transient-logger.service.ts +++ b/integration/scopes/src/nested-transient/transient-logger.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { NestedTransientService } from './nested-transient.service'; +import { NestedTransientService } from './nested-transient.service.js'; @Injectable({ scope: Scope.TRANSIENT }) export class TransientLoggerService { diff --git a/integration/scopes/src/request-chain/helper/helper.module.ts b/integration/scopes/src/request-chain/helper/helper.module.ts index 1c154ea44cb..fa31021e32d 100644 --- a/integration/scopes/src/request-chain/helper/helper.module.ts +++ b/integration/scopes/src/request-chain/helper/helper.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { HelperService } from './helper.service'; +import { HelperService } from './helper.service.js'; @Module({ providers: [HelperService], diff --git a/integration/scopes/src/request-chain/interceptors/logging.interceptor.ts b/integration/scopes/src/request-chain/interceptors/logging.interceptor.ts index 008a6999373..17c83c7e82d 100644 --- a/integration/scopes/src/request-chain/interceptors/logging.interceptor.ts +++ b/integration/scopes/src/request-chain/interceptors/logging.interceptor.ts @@ -5,7 +5,7 @@ import { NestInterceptor, } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { HelperService } from '../helper/helper.service'; +import { HelperService } from '../helper/helper.service.js'; @Injectable() export class LoggingInterceptor implements NestInterceptor { diff --git a/integration/scopes/src/request-chain/request-chain.controller.ts b/integration/scopes/src/request-chain/request-chain.controller.ts index 03813352ddb..f6b22f1caef 100644 --- a/integration/scopes/src/request-chain/request-chain.controller.ts +++ b/integration/scopes/src/request-chain/request-chain.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, UseInterceptors } from '@nestjs/common'; -import { LoggingInterceptor } from './interceptors/logging.interceptor'; -import { RequestChainService } from './request-chain.service'; +import { LoggingInterceptor } from './interceptors/logging.interceptor.js'; +import { RequestChainService } from './request-chain.service.js'; @Controller('hello') export class RequestChainController { diff --git a/integration/scopes/src/request-chain/request-chain.module.ts b/integration/scopes/src/request-chain/request-chain.module.ts index eba8f451d9f..e5478eef781 100644 --- a/integration/scopes/src/request-chain/request-chain.module.ts +++ b/integration/scopes/src/request-chain/request-chain.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { HelperModule } from './helper/helper.module'; -import { RequestChainController } from './request-chain.controller'; -import { RequestChainService } from './request-chain.service'; +import { HelperModule } from './helper/helper.module.js'; +import { RequestChainController } from './request-chain.controller.js'; +import { RequestChainService } from './request-chain.service.js'; @Module({ imports: [HelperModule], diff --git a/integration/scopes/src/request-chain/request-chain.service.ts b/integration/scopes/src/request-chain/request-chain.service.ts index c8c00068d3f..bf526aa498f 100644 --- a/integration/scopes/src/request-chain/request-chain.service.ts +++ b/integration/scopes/src/request-chain/request-chain.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { HelperService } from './helper/helper.service'; +import { HelperService } from './helper/helper.service.js'; @Injectable() export class RequestChainService { diff --git a/integration/scopes/src/resolve-scoped/logger.service.ts b/integration/scopes/src/resolve-scoped/logger.service.ts index 36f8b034bde..e53dab371d4 100644 --- a/integration/scopes/src/resolve-scoped/logger.service.ts +++ b/integration/scopes/src/resolve-scoped/logger.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable, Scope } from '@nestjs/common'; -import { LOGGER_PROVIDER } from './logger.provider'; +import { LOGGER_PROVIDER } from './logger.provider.js'; @Injectable({ scope: Scope.TRANSIENT }) export class LoggerService { diff --git a/integration/scopes/src/resolve-scoped/request-logger.service.ts b/integration/scopes/src/resolve-scoped/request-logger.service.ts index 84b59563b49..f226be06ed0 100644 --- a/integration/scopes/src/resolve-scoped/request-logger.service.ts +++ b/integration/scopes/src/resolve-scoped/request-logger.service.ts @@ -1,5 +1,5 @@ import { Injectable, Scope } from '@nestjs/common'; -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service.js'; @Injectable({ scope: Scope.REQUEST }) export class RequestLoggerService { diff --git a/integration/scopes/src/transient/hello.controller.ts b/integration/scopes/src/transient/hello.controller.ts index 5b4af7bf215..94d20f26cf3 100644 --- a/integration/scopes/src/transient/hello.controller.ts +++ b/integration/scopes/src/transient/hello.controller.ts @@ -5,11 +5,11 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { Guard } from './guards/request-scoped.guard'; -import { HelloService } from './hello.service'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UserByIdPipe } from './users/user-by-id.pipe'; -import { UsersService } from './users/users.service'; +import { Guard } from './guards/request-scoped.guard.js'; +import { HelloService } from './hello.service.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; +import { UsersService } from './users/users.service.js'; @Controller('hello') export class HelloController { diff --git a/integration/scopes/src/transient/hello.module.ts b/integration/scopes/src/transient/hello.module.ts index 6cdc92c5f27..db10f010597 100644 --- a/integration/scopes/src/transient/hello.module.ts +++ b/integration/scopes/src/transient/hello.module.ts @@ -1,8 +1,8 @@ import { DynamicModule, Inject, Module, Provider } from '@nestjs/common'; -import { HelloController } from './hello.controller'; -import { HelloService } from './hello.service'; -import { TestController } from './test.controller'; -import { UsersService } from './users/users.service'; +import { HelloController } from './hello.controller.js'; +import { HelloService } from './hello.service.js'; +import { TestController } from './test.controller.js'; +import { UsersService } from './users/users.service.js'; @Module({ controllers: [HelloController, TestController], diff --git a/integration/scopes/src/transient/test.controller.ts b/integration/scopes/src/transient/test.controller.ts index 572ea13a90f..ac1002a7898 100644 --- a/integration/scopes/src/transient/test.controller.ts +++ b/integration/scopes/src/transient/test.controller.ts @@ -5,9 +5,9 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { Guard } from './guards/request-scoped.guard'; -import { Interceptor } from './interceptors/logging.interceptor'; -import { UserByIdPipe } from './users/user-by-id.pipe'; +import { Guard } from './guards/request-scoped.guard.js'; +import { Interceptor } from './interceptors/logging.interceptor.js'; +import { UserByIdPipe } from './users/user-by-id.pipe.js'; @Controller('test') export class TestController { diff --git a/integration/scopes/tsconfig.json b/integration/scopes/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/scopes/tsconfig.json +++ b/integration/scopes/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/send-files/e2e/express.spec.ts b/integration/send-files/e2e/express.spec.ts index c0b8e566194..b12017c6272 100644 --- a/integration/send-files/e2e/express.spec.ts +++ b/integration/send-files/e2e/express.spec.ts @@ -3,16 +3,15 @@ import { NestExpressApplication, } from '@nestjs/platform-express'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { readFileSync } from 'fs'; import { join } from 'path'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; import { getHttpBaseOptions, sendCanceledHttpRequest, sendHttpRequest, -} from './utils'; +} from './utils.js'; const readme = readFileSync(join(process.cwd(), 'Readme.md')); const readmeString = readme.toString(); @@ -34,7 +33,7 @@ describe('Express FileSend', () => { .get('/file/stream/') .expect(200) .expect(res => { - expect(res.body.toString()).to.be.eq(readmeString); + expect(res.body.toString()).toBe(readmeString); }); }); it('should return a file from a buffer', async () => { @@ -42,7 +41,7 @@ describe('Express FileSend', () => { .get('/file/buffer') .expect(200) .expect(res => { - expect(res.body.toString()).to.be.eq(readmeString); + expect(res.body.toString()).toBe(readmeString); }); }); it('should not stream a non-file', async () => { @@ -56,7 +55,7 @@ describe('Express FileSend', () => { .get('/file/rxjs/stream/') .expect(200) .expect(res => { - expect(res.body.toString()).to.be.eq(readmeString); + expect(res.body.toString()).toBe(readmeString); }); }); it('should return a file with correct headers', async () => { @@ -67,21 +66,18 @@ describe('Express FileSend', () => { .expect('Content-Disposition', 'attachment; filename="Readme.md"') .expect('Content-Length', readme.byteLength.toString()) .expect(res => { - expect(res.text).to.be.eq(readmeString); + expect(res.text).toBe(readmeString); }); }); it('should return an error if the file does not exist', async () => { return request(app.getHttpServer()).get('/file/not/exist').expect(400); }); // TODO: temporarily turned off (flaky test) - it.skip( - 'should allow for the client to end the response and be able to make another', - async () => { - await app.listen(0); - const url = await getHttpBaseOptions(app); - await sendCanceledHttpRequest(new URL('/file/slow', url)); - const res = await sendHttpRequest(new URL('/file/stream', url)); - expect(res.statusCode).to.be.eq(200); - }, - ).timeout(5000); + it.skip('should allow for the client to end the response and be able to make another', async () => { + await app.listen(0); + const url = await getHttpBaseOptions(app); + await sendCanceledHttpRequest(new URL('/file/slow', url)); + const res = await sendHttpRequest(new URL('/file/stream', url)); + expect(res.statusCode).toBe(200); + }); }); diff --git a/integration/send-files/e2e/fastify.spec.ts b/integration/send-files/e2e/fastify.spec.ts index cec87363467..2dbfe524a07 100644 --- a/integration/send-files/e2e/fastify.spec.ts +++ b/integration/send-files/e2e/fastify.spec.ts @@ -3,10 +3,9 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { readFileSync } from 'fs'; import { join } from 'path'; -import { AppModule } from '../src/app.module'; +import { AppModule } from '../src/app.module.js'; const readme = readFileSync(join(process.cwd(), 'Readme.md')); const readmeString = readme.toString(); @@ -29,8 +28,9 @@ describe('Fastify FileSend', () => { method: 'GET', url: '/file/stream', }) - .then(({ payload }) => { - expect(payload.toString()).to.be.eq(readmeString); + .then(({ payload, statusCode }) => { + expect(statusCode).toBe(200); + expect(payload.toString()).toBe(readmeString); }); }); it('should return a file from a buffer', async () => { @@ -39,8 +39,9 @@ describe('Fastify FileSend', () => { method: 'GET', url: '/file/buffer', }) - .then(({ payload }) => { - expect(payload.toString()).to.be.eq(readmeString); + .then(({ payload, statusCode }) => { + expect(statusCode).toBe(200); + expect(payload.toString()).toBe(readmeString); }); }); /** @@ -55,7 +56,7 @@ describe('Fastify FileSend', () => { method: 'get', }) .then(({ payload }) => { - expect(payload).to.be.eq({ value: 'Hello world' }); + expect(payload).toBe({ value: 'Hello world' }); }); }); it('should return a file from an RxJS stream', async () => { @@ -64,21 +65,36 @@ describe('Fastify FileSend', () => { method: 'GET', url: '/file/rxjs/stream', }) - .then(({ payload }) => { - expect(payload.toString()).to.be.eq(readmeString); + .then(({ payload, statusCode }) => { + expect(statusCode).toBe(200); + expect(payload.toString()).toBe(readmeString); }); }); it('should return a file with correct headers', async () => { return app .inject({ url: '/file/with/headers', method: 'get' }) .then(({ statusCode, headers, payload }) => { - expect(statusCode).to.equal(200); - expect(headers['content-type']).to.equal('text/markdown'); - expect(headers['content-disposition']).to.equal( + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/markdown'); + expect(headers['content-disposition']).toBe( 'attachment; filename="Readme.md"', ); - expect(headers['content-length']).to.equal(`${readme.byteLength}`); - expect(payload).to.equal(readmeString); + expect(headers['content-length']).toBe(`${readme.byteLength}`); + expect(payload).toBe(readmeString); }); }); + it('should return an error if the file does not exist', async () => { + return app + .inject({ + method: 'GET', + url: '/file/not/exist', + }) + .then(({ statusCode }) => { + expect(statusCode).toBe(500); + }); + }); + + afterEach(async () => { + await app.close(); + }); }); diff --git a/integration/send-files/src/app.controller.ts b/integration/send-files/src/app.controller.ts index 69fa20ec069..0c3cd078c7e 100644 --- a/integration/send-files/src/app.controller.ts +++ b/integration/send-files/src/app.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, StreamableFile } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { AppService } from './app.service'; -import { NonFile } from './non-file'; +import { AppService } from './app.service.js'; +import { NonFile } from './non-file.js'; @Controller() export class AppController { diff --git a/integration/send-files/src/app.module.ts b/integration/send-files/src/app.module.ts index 7845d045f8d..7abec95862b 100644 --- a/integration/send-files/src/app.module.ts +++ b/integration/send-files/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { AppController } from './app.controller.js'; +import { AppService } from './app.service.js'; @Module({ controllers: [AppController], diff --git a/integration/send-files/src/app.service.ts b/integration/send-files/src/app.service.ts index e5f920c48b8..6d24d80106a 100644 --- a/integration/send-files/src/app.service.ts +++ b/integration/send-files/src/app.service.ts @@ -4,7 +4,7 @@ import { createReadStream, readFileSync } from 'fs'; import { join } from 'path'; import { Observable, of } from 'rxjs'; import { Readable } from 'stream'; -import { NonFile } from './non-file'; +import { NonFile } from './non-file.js'; @Injectable() export class AppService { diff --git a/integration/send-files/tsconfig.json b/integration/send-files/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/send-files/tsconfig.json +++ b/integration/send-files/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/testing-module-override/circular-dependency/a.module.ts b/integration/testing-module-override/circular-dependency/a.module.ts index 6540ded32d4..4060b4d1a4a 100644 --- a/integration/testing-module-override/circular-dependency/a.module.ts +++ b/integration/testing-module-override/circular-dependency/a.module.ts @@ -1,5 +1,5 @@ import { Injectable, Module, forwardRef } from '@nestjs/common'; -import { BModule } from './b.module'; +import { BModule } from './b.module.js'; @Injectable() export class AProvider {} diff --git a/integration/testing-module-override/circular-dependency/b.module.ts b/integration/testing-module-override/circular-dependency/b.module.ts index 2f4d5baf6ce..ce8252f3465 100644 --- a/integration/testing-module-override/circular-dependency/b.module.ts +++ b/integration/testing-module-override/circular-dependency/b.module.ts @@ -1,5 +1,5 @@ import { Injectable, Module, forwardRef } from '@nestjs/common'; -import { AModule } from './a.module'; +import { AModule } from './a.module.js'; @Injectable() export class BProvider {} diff --git a/integration/testing-module-override/e2e/modules-override.spec.ts b/integration/testing-module-override/e2e/modules-override.spec.ts index 234767e1824..0928f0a576d 100644 --- a/integration/testing-module-override/e2e/modules-override.spec.ts +++ b/integration/testing-module-override/e2e/modules-override.spec.ts @@ -8,9 +8,8 @@ import { } from '@nestjs/common'; import { LazyModuleLoader } from '@nestjs/core'; import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import { AModule, AProvider } from '../circular-dependency/a.module'; -import { BModule, BProvider } from '../circular-dependency/b.module'; +import { AModule, AProvider } from '../circular-dependency/a.module.js'; +import { BModule, BProvider } from '../circular-dependency/b.module.js'; describe('Modules overriding', () => { describe('Top-level module', () => { @@ -44,10 +43,10 @@ describe('Modules overriding', () => { it('should override top-level modules using testing module builder', () => { expect(() => testingModule.get(ControllerOverwritten), - ).to.throw(); + ).toThrow(); expect( testingModule.get(ControllerOverride), - ).to.be.an.instanceof(ControllerOverride); + ).toBeInstanceOf(ControllerOverride); }); }); @@ -88,10 +87,10 @@ describe('Modules overriding', () => { it('should override dynamic modules using testing module builder', () => { expect(() => testingModule.get(ControllerOverwritten), - ).to.throw(); + ).toThrow(); expect( testingModule.get(ControllerOverride), - ).to.be.an.instanceof(ControllerOverride); + ).toBeInstanceOf(ControllerOverride); }); }); @@ -126,16 +125,12 @@ describe('Modules overriding', () => { }); it('should override top-level modules using testing module builder', () => { - expect(testingModule.get(AProvider)).to.be.an.instanceof( - AProvider, - ); - expect(() => testingModule.get(BProvider)).to.throw(); - expect(testingModule.get(CProvider)).to.be.an.instanceof( - CProvider, - ); + expect(testingModule.get(AProvider)).toBeInstanceOf(AProvider); + expect(() => testingModule.get(BProvider)).toThrow(); + expect(testingModule.get(CProvider)).toBeInstanceOf(CProvider); expect( testingModule.get(BProviderOverride), - ).to.be.an.instanceof(BProviderOverride); + ).toBeInstanceOf(BProviderOverride); }); }); @@ -177,12 +172,12 @@ describe('Modules overriding', () => { testingModule.get( OverrideNestedModuleController, ), - ).to.be.an.instanceof(OverrideNestedModuleController); + ).toBeInstanceOf(OverrideNestedModuleController); expect(() => testingModule.get( OverwrittenNestedModuleController, ), - ).to.throw(); + ).toThrow(); }); }); @@ -252,7 +247,7 @@ describe('Modules overriding', () => { it('should override lazy loaded modules using testing module builder', async () => { const result = await testingModule.get(AppService).value(); - expect(result).to.be.equal('override lazy'); + expect(result).toBe('override lazy'); }); }); @@ -299,10 +294,10 @@ describe('Modules overriding', () => { it('should override global modules using testing module builder', () => { expect( testingModule.get(OverrideProvider), - ).to.be.an.instanceof(OverrideProvider); + ).toBeInstanceOf(OverrideProvider); expect(() => testingModule.get(OverwrittenProvider), - ).to.throw(); + ).toThrow(); }); }); }); diff --git a/integration/testing-module-override/tsconfig.json b/integration/testing-module-override/tsconfig.json index c43c3d1b1be..e747ddd38ca 100644 --- a/integration/testing-module-override/tsconfig.json +++ b/integration/testing-module-override/tsconfig.json @@ -1,12 +1,14 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": true, "removeComments": true, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", diff --git a/integration/typeorm/e2e/typeorm-async-class.spec.ts b/integration/typeorm/e2e/typeorm-async-class.spec.ts index d3ac3275dda..976831b4c14 100644 --- a/integration/typeorm/e2e/typeorm-async-class.spec.ts +++ b/integration/typeorm/e2e/typeorm-async-class.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AsyncOptionsClassModule } from '../src/async-class-options.module'; +import request from 'supertest'; +import { AsyncOptionsClassModule } from '../src/async-class-options.module.js'; describe('TypeOrm (async configuration)', () => { let server; diff --git a/integration/typeorm/e2e/typeorm-async-existing.spec.ts b/integration/typeorm/e2e/typeorm-async-existing.spec.ts index bb1f9cc46a4..78eb87d7b51 100644 --- a/integration/typeorm/e2e/typeorm-async-existing.spec.ts +++ b/integration/typeorm/e2e/typeorm-async-existing.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AsyncOptionsExistingModule } from '../src/async-existing-options.module'; +import request from 'supertest'; +import { AsyncOptionsExistingModule } from '../src/async-existing-options.module.js'; describe('TypeOrm (async configuration)', () => { let server; diff --git a/integration/typeorm/e2e/typeorm-async-options.spec.ts b/integration/typeorm/e2e/typeorm-async-options.spec.ts index b506139797b..ee6d8a0f9d5 100644 --- a/integration/typeorm/e2e/typeorm-async-options.spec.ts +++ b/integration/typeorm/e2e/typeorm-async-options.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AsyncOptionsFactoryModule } from '../src/async-options.module'; +import request from 'supertest'; +import { AsyncOptionsFactoryModule } from '../src/async-options.module.js'; describe('TypeOrm (async configuration)', () => { let server; diff --git a/integration/typeorm/e2e/typeorm-async.spec.ts b/integration/typeorm/e2e/typeorm-async.spec.ts index 13410d7988f..ccb5ad3db9c 100644 --- a/integration/typeorm/e2e/typeorm-async.spec.ts +++ b/integration/typeorm/e2e/typeorm-async.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AsyncApplicationModule } from '../src/app-async.module'; +import request from 'supertest'; +import { AsyncApplicationModule } from '../src/app-async.module.js'; describe('TypeOrm (async configuration)', () => { let server; diff --git a/integration/typeorm/e2e/typeorm.spec.ts b/integration/typeorm/e2e/typeorm.spec.ts index 95871390360..1e2fa712a16 100644 --- a/integration/typeorm/e2e/typeorm.spec.ts +++ b/integration/typeorm/e2e/typeorm.spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('TypeOrm', () => { let server; diff --git a/integration/typeorm/src/app-async.module.ts b/integration/typeorm/src/app-async.module.ts index 891d7a45629..b14174a7c63 100644 --- a/integration/typeorm/src/app-async.module.ts +++ b/integration/typeorm/src/app-async.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { DatabaseModule } from './database.module'; -import { PhotoModule } from './photo/photo.module'; +import { DatabaseModule } from './database.module.js'; +import { PhotoModule } from './photo/photo.module.js'; @Module({ imports: [DatabaseModule.forRoot(), PhotoModule], diff --git a/integration/typeorm/src/app.module.ts b/integration/typeorm/src/app.module.ts index 36fc5b8356b..342f44204d9 100644 --- a/integration/typeorm/src/app.module.ts +++ b/integration/typeorm/src/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { Photo } from './photo/photo.entity'; -import { PhotoModule } from './photo/photo.module'; +import { Photo } from './photo/photo.entity.js'; +import { PhotoModule } from './photo/photo.module.js'; @Module({ imports: [ diff --git a/integration/typeorm/src/async-class-options.module.ts b/integration/typeorm/src/async-class-options.module.ts index 37ed3686535..334eca5323f 100644 --- a/integration/typeorm/src/async-class-options.module.ts +++ b/integration/typeorm/src/async-class-options.module.ts @@ -4,8 +4,8 @@ import { TypeOrmModuleOptions, TypeOrmOptionsFactory, } from '@nestjs/typeorm'; -import { Photo } from './photo/photo.entity'; -import { PhotoModule } from './photo/photo.module'; +import { Photo } from './photo/photo.entity.js'; +import { PhotoModule } from './photo/photo.module.js'; class ConfigService implements TypeOrmOptionsFactory { createTypeOrmOptions(): TypeOrmModuleOptions { diff --git a/integration/typeorm/src/async-existing-options.module.ts b/integration/typeorm/src/async-existing-options.module.ts index 016c35406b5..54e482710fe 100644 --- a/integration/typeorm/src/async-existing-options.module.ts +++ b/integration/typeorm/src/async-existing-options.module.ts @@ -4,8 +4,8 @@ import { TypeOrmModuleOptions, TypeOrmOptionsFactory, } from '@nestjs/typeorm'; -import { Photo } from './photo/photo.entity'; -import { PhotoModule } from './photo/photo.module'; +import { Photo } from './photo/photo.entity.js'; +import { PhotoModule } from './photo/photo.module.js'; class ConfigService implements TypeOrmOptionsFactory { createTypeOrmOptions(): TypeOrmModuleOptions { diff --git a/integration/typeorm/src/async-options.module.ts b/integration/typeorm/src/async-options.module.ts index cda3a84d6c9..ba10b8937a0 100644 --- a/integration/typeorm/src/async-options.module.ts +++ b/integration/typeorm/src/async-options.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { Photo } from './photo/photo.entity'; -import { PhotoModule } from './photo/photo.module'; +import { Photo } from './photo/photo.entity.js'; +import { PhotoModule } from './photo/photo.module.js'; @Module({ imports: [ diff --git a/integration/typeorm/src/database.module.ts b/integration/typeorm/src/database.module.ts index 74110f3f12c..b0ac233f1f3 100644 --- a/integration/typeorm/src/database.module.ts +++ b/integration/typeorm/src/database.module.ts @@ -1,6 +1,6 @@ import { DynamicModule, Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { Photo } from './photo/photo.entity'; +import { Photo } from './photo/photo.entity.js'; @Module({}) export class DatabaseModule { diff --git a/integration/typeorm/src/main.ts b/integration/typeorm/src/main.ts index c7f4a18713e..b96604fb84c 100644 --- a/integration/typeorm/src/main.ts +++ b/integration/typeorm/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/integration/typeorm/src/photo/photo.controller.ts b/integration/typeorm/src/photo/photo.controller.ts index b693de7bb72..d137ed57929 100644 --- a/integration/typeorm/src/photo/photo.controller.ts +++ b/integration/typeorm/src/photo/photo.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Post } from '@nestjs/common'; -import { Photo } from './photo.entity'; -import { PhotoService } from './photo.service'; +import { Photo } from './photo.entity.js'; +import { PhotoService } from './photo.service.js'; @Controller('photo') export class PhotoController { diff --git a/integration/typeorm/src/photo/photo.module.ts b/integration/typeorm/src/photo/photo.module.ts index b82ece49c0d..d122a7e5ef5 100644 --- a/integration/typeorm/src/photo/photo.module.ts +++ b/integration/typeorm/src/photo/photo.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; -import { PhotoService } from './photo.service'; -import { PhotoController } from './photo.controller'; +import { PhotoService } from './photo.service.js'; +import { PhotoController } from './photo.controller.js'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { Photo } from './photo.entity'; +import { Photo } from './photo.entity.js'; @Module({ imports: [TypeOrmModule.forFeature([Photo])], diff --git a/integration/typeorm/src/photo/photo.service.ts b/integration/typeorm/src/photo/photo.service.ts index bbe7b892a44..977706e6799 100644 --- a/integration/typeorm/src/photo/photo.service.ts +++ b/integration/typeorm/src/photo/photo.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { Photo } from './photo.entity'; +import { Photo } from './photo.entity.js'; @Injectable() export class PhotoService { diff --git a/integration/typeorm/tsconfig.json b/integration/typeorm/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/typeorm/tsconfig.json +++ b/integration/typeorm/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/versioning/e2e/custom-versioning-fastify.spec.ts b/integration/versioning/e2e/custom-versioning-fastify.spec.ts index a428136a67a..16bdd54c88e 100644 --- a/integration/versioning/e2e/custom-versioning-fastify.spec.ts +++ b/integration/versioning/e2e/custom-versioning-fastify.spec.ts @@ -5,8 +5,8 @@ import { } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; import { FastifyRequest } from 'fastify'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Custom Versioning (fastify)', () => { const extractor = (request: FastifyRequest): string | string[] => { @@ -24,7 +24,7 @@ describe('Custom Versioning (fastify)', () => { // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -497,14 +497,14 @@ describe('Custom Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -960,7 +960,7 @@ describe('Custom Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/custom-versioning.spec.ts b/integration/versioning/e2e/custom-versioning.spec.ts index 73e56c41c2c..0726bf79892 100644 --- a/integration/versioning/e2e/custom-versioning.spec.ts +++ b/integration/versioning/e2e/custom-versioning.spec.ts @@ -1,8 +1,8 @@ import { INestApplication, VersioningType } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { Request } from 'express'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Custom Versioning', () => { const extractor = (request: Request): string | string[] => { @@ -21,7 +21,7 @@ describe('Custom Versioning', () => { // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -380,14 +380,14 @@ describe('Custom Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -705,7 +705,7 @@ describe('Custom Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/default-versioning.spec.ts b/integration/versioning/e2e/default-versioning.spec.ts index d1b62e0ec37..c5c3759a85e 100644 --- a/integration/versioning/e2e/default-versioning.spec.ts +++ b/integration/versioning/e2e/default-versioning.spec.ts @@ -4,8 +4,8 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; /** * `.enableVersioning()` uses `VersioningType.URI` type by default @@ -16,7 +16,7 @@ describe('Default Versioning behavior', () => { // ======================================================================== // describe('Express', () => { let app: INestApplication; - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -48,7 +48,7 @@ describe('Default Versioning behavior', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); @@ -56,7 +56,7 @@ describe('Default Versioning behavior', () => { // ======================================================================== // describe('Fastify', () => { let app: INestApplication; - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -91,7 +91,7 @@ describe('Default Versioning behavior', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/header-versioning-fastify.spec.ts b/integration/versioning/e2e/header-versioning-fastify.spec.ts index a79c203eee5..17044083333 100644 --- a/integration/versioning/e2e/header-versioning-fastify.spec.ts +++ b/integration/versioning/e2e/header-versioning-fastify.spec.ts @@ -4,15 +4,15 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Header Versioning (fastify)', () => { let app: INestApplication; // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -338,14 +338,14 @@ describe('Header Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -666,7 +666,7 @@ describe('Header Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/header-versioning.spec.ts b/integration/versioning/e2e/header-versioning.spec.ts index 3be9341c96b..11ad6e8bea3 100644 --- a/integration/versioning/e2e/header-versioning.spec.ts +++ b/integration/versioning/e2e/header-versioning.spec.ts @@ -1,14 +1,14 @@ import { INestApplication, VersioningType } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Header Versioning', () => { let app: INestApplication; // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -331,14 +331,14 @@ describe('Header Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -656,7 +656,7 @@ describe('Header Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/media-type-versioning-fastify.spec.ts b/integration/versioning/e2e/media-type-versioning-fastify.spec.ts index 15cb16dfbeb..c9cef8b4a9e 100644 --- a/integration/versioning/e2e/media-type-versioning-fastify.spec.ts +++ b/integration/versioning/e2e/media-type-versioning-fastify.spec.ts @@ -4,15 +4,15 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Media Type Versioning (fastify)', () => { let app: INestApplication; // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -338,14 +338,14 @@ describe('Media Type Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -666,7 +666,7 @@ describe('Media Type Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/media-type-versioning.spec.ts b/integration/versioning/e2e/media-type-versioning.spec.ts index fd2b39f5d53..b333a1a1ac3 100644 --- a/integration/versioning/e2e/media-type-versioning.spec.ts +++ b/integration/versioning/e2e/media-type-versioning.spec.ts @@ -1,14 +1,14 @@ import { INestApplication, VersioningType } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('Media Type Versioning', () => { let app: INestApplication; // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -331,14 +331,14 @@ describe('Media Type Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -656,7 +656,7 @@ describe('Media Type Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/uri-versioning-fastify.spec.ts b/integration/versioning/e2e/uri-versioning-fastify.spec.ts index 98b94dade52..14f052e7bbc 100644 --- a/integration/versioning/e2e/uri-versioning-fastify.spec.ts +++ b/integration/versioning/e2e/uri-versioning-fastify.spec.ts @@ -4,15 +4,15 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('URI Versioning (fastify)', () => { let app: INestApplication; // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -181,14 +181,14 @@ describe('URI Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -358,7 +358,7 @@ describe('URI Versioning (fastify)', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/e2e/uri-versioning.spec.ts b/integration/versioning/e2e/uri-versioning.spec.ts index 1bb178eafe9..f31f743f462 100644 --- a/integration/versioning/e2e/uri-versioning.spec.ts +++ b/integration/versioning/e2e/uri-versioning.spec.ts @@ -1,14 +1,14 @@ import { INestApplication, VersioningType } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; describe('URI Versioning', () => { let app: INestApplication; // ======================================================================== // describe('without global default version', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -174,14 +174,14 @@ describe('URI Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global default version: "1"', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -348,14 +348,14 @@ describe('URI Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with the global prefix enabled and an excluded route', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -414,14 +414,14 @@ describe('URI Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); // ======================================================================== // describe('with middleware applied', () => { - before(async () => { + beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }).compile(); @@ -477,7 +477,7 @@ describe('URI Versioning', () => { }); }); - after(async () => { + afterAll(async () => { await app.close(); }); }); diff --git a/integration/versioning/src/app.module.ts b/integration/versioning/src/app.module.ts index d1483685af7..109df94f8a1 100644 --- a/integration/versioning/src/app.module.ts +++ b/integration/versioning/src/app.module.ts @@ -1,14 +1,14 @@ import { MiddlewareConsumer, Module } from '@nestjs/common'; -import { AppV1Controller } from './app-v1.controller'; -import { AppV2Controller } from './app-v2.controller'; -import { MiddlewareController } from './middleware.controller'; -import { MultipleMiddlewareVersionController } from './multiple-middleware.controller'; -import { MultipleVersionController } from './multiple.controller'; -import { VersionNeutralMiddlewareController } from './neutral-middleware.controller'; -import { VersionNeutralController } from './neutral.controller'; -import { NoVersioningController } from './no-versioning.controller'; -import { OverridePartialController } from './override-partial.controller'; -import { OverrideController } from './override.controller'; +import { AppV1Controller } from './app-v1.controller.js'; +import { AppV2Controller } from './app-v2.controller.js'; +import { MiddlewareController } from './middleware.controller.js'; +import { MultipleMiddlewareVersionController } from './multiple-middleware.controller.js'; +import { MultipleVersionController } from './multiple.controller.js'; +import { VersionNeutralMiddlewareController } from './neutral-middleware.controller.js'; +import { VersionNeutralController } from './neutral.controller.js'; +import { NoVersioningController } from './no-versioning.controller.js'; +import { OverridePartialController } from './override-partial.controller.js'; +import { OverrideController } from './override.controller.js'; @Module({ imports: [], diff --git a/integration/versioning/src/main.ts b/integration/versioning/src/main.ts index 1d316cac8f8..91cba3b57a1 100644 --- a/integration/versioning/src/main.ts +++ b/integration/versioning/src/main.ts @@ -1,6 +1,6 @@ import { VersioningType } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/integration/versioning/tsconfig.json b/integration/versioning/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/versioning/tsconfig.json +++ b/integration/versioning/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/integration/websockets/e2e/error-gateway.spec.ts b/integration/websockets/e2e/error-gateway.spec.ts index 5e5335a00b3..b7bc3e8ce91 100644 --- a/integration/websockets/e2e/error-gateway.spec.ts +++ b/integration/websockets/e2e/error-gateway.spec.ts @@ -1,8 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { io } from 'socket.io-client'; -import { ErrorGateway } from '../src/error.gateway'; +import { ErrorGateway } from '../src/error.gateway.js'; describe('ErrorGateway', () => { let app: INestApplication; @@ -25,7 +24,7 @@ describe('ErrorGateway', () => { await new Promise(resolve => ws.on('exception', error => { - expect(error).to.be.eql({ + expect(error).toEqual({ status: 'error', message: 'test', cause: { diff --git a/integration/websockets/e2e/gateway-ack.spec.ts b/integration/websockets/e2e/gateway-ack.spec.ts index 6ae5658b592..81730ba1902 100644 --- a/integration/websockets/e2e/gateway-ack.spec.ts +++ b/integration/websockets/e2e/gateway-ack.spec.ts @@ -1,8 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { io } from 'socket.io-client'; -import { AckGateway } from '../src/ack.gateway'; +import { AckGateway } from '../src/ack.gateway.js'; async function createNestApp(...gateways): Promise { const testingModule = await Test.createTestingModule({ @@ -22,7 +21,7 @@ describe('WebSocketGateway (ack)', () => { ws = io('http://localhost:8080'); await new Promise(resolve => ws.emit('push', { test: 'test' }, data => { - expect(data).to.be.eql('pong'); + expect(data).toEqual('pong'); resolve(); }), ); @@ -35,7 +34,7 @@ describe('WebSocketGateway (ack)', () => { ws = io('http://localhost:8080'); await new Promise(resolve => ws.emit('push', data => { - expect(data).to.be.eql('pong'); + expect(data).toEqual('pong'); resolve(); }), ); @@ -50,7 +49,7 @@ describe('WebSocketGateway (ack)', () => { await new Promise(resolve => ws.emit('manual-ack', payload, response => { - expect(response).to.eql({ status: 'success', data: payload }); + expect(response).toEqual({ status: 'success', data: payload }); resolve(); }), ); @@ -65,7 +64,7 @@ describe('WebSocketGateway (ack)', () => { await new Promise(resolve => ws.emit('manual-ack', payload, response => { - expect(response).to.eql({ + expect(response).toEqual({ status: 'error', message: 'Operation failed', }); diff --git a/integration/websockets/e2e/gateway.spec.ts b/integration/websockets/e2e/gateway.spec.ts index 14aff034ab6..7ed5699f04c 100644 --- a/integration/websockets/e2e/gateway.spec.ts +++ b/integration/websockets/e2e/gateway.spec.ts @@ -1,12 +1,11 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; import { EventSource } from 'eventsource'; import { io } from 'socket.io-client'; -import { AppController as LongConnectionController } from '../../nest-application/sse/src/app.controller'; -import { ApplicationGateway } from '../src/app.gateway'; -import { NamespaceGateway } from '../src/namespace.gateway'; -import { ServerGateway } from '../src/server.gateway'; +import { AppController as LongConnectionController } from '../../nest-application/sse/src/app.controller.js'; +import { ApplicationGateway } from '../src/app.gateway.js'; +import { NamespaceGateway } from '../src/namespace.gateway.js'; +import { ServerGateway } from '../src/server.gateway.js'; async function createNestApp(...gateways): Promise { const testingModule = await Test.createTestingModule({ @@ -29,7 +28,7 @@ describe('WebSocketGateway', () => { }); await new Promise(resolve => ws.on('pop', data => { - expect(data.test).to.be.eql('test'); + expect(data.test).toEqual('test'); resolve(); }), ); @@ -45,7 +44,7 @@ describe('WebSocketGateway', () => { }); await new Promise(resolve => ws.on('pop', data => { - expect(data.test).to.be.eql('test'); + expect(data.test).toEqual('test'); resolve(); }), ); @@ -62,7 +61,7 @@ describe('WebSocketGateway', () => { }); await new Promise(resolve => ws.on('pop', data => { - expect(data.test).to.be.eql('test'); + expect(data.test).toEqual('test'); resolve(); }), ); @@ -78,7 +77,7 @@ describe('WebSocketGateway', () => { }); await new Promise(resolve => ws.on('popClient', data => { - expect(data.path).to.be.eql('getClient'); + expect(data.path).toEqual('getClient'); resolve(); }), ); @@ -94,51 +93,49 @@ describe('WebSocketGateway', () => { }); await new Promise(resolve => ws.on('exception', data => { - expect(data.pattern).to.be.eql('getClientWithError'); + expect(data.pattern).toEqual('getClientWithError'); resolve(); }), ); }); describe('shared server for WS and Long-Running connections', () => { - afterEach(() => {}); - it('should block application shutdown', function (done) { - let eventSource: EventSource; - - void (async () => { - this.timeout(30000); - - setTimeout(() => { - const instance = testingModule.get(ServerGateway); - expect(instance.onApplicationShutdown.called).to.be.false; - eventSource.close(); - done(); - }, 25000); - - const testingModule = await Test.createTestingModule({ - providers: [ServerGateway], - controllers: [LongConnectionController], - }).compile(); - app = testingModule.createNestApplication(); - - await app.listen(3000); - - ws = io(`http://localhost:3000`); - eventSource = new EventSource(`http://localhost:3000/sse`); - - await new Promise((resolve, reject) => { - ws.on('connect', resolve); - ws.on('error', reject); - }); - - await new Promise((resolve, reject) => { - eventSource.onmessage = resolve; - eventSource.onerror = reject; - }); - - await app.close(); - })(); - }); + it('should block application shutdown', () => + new Promise(done => { + let eventSource: EventSource; + + void (async () => { + setTimeout(() => { + const instance = testingModule.get(ServerGateway); + expect(instance.onApplicationShutdown).not.toHaveBeenCalled(); + eventSource.close(); + done(); + }, 25000); + + const testingModule = await Test.createTestingModule({ + providers: [ServerGateway], + controllers: [LongConnectionController], + }).compile(); + app = testingModule.createNestApplication(); + + await app.listen(3000); + + ws = io(`http://localhost:3000`); + eventSource = new EventSource(`http://localhost:3000/sse`); + + await new Promise((resolve, reject) => { + ws.on('connect', resolve); + ws.on('error', reject); + }); + + await new Promise((resolve, reject) => { + eventSource.onmessage = resolve; + eventSource.onerror = reject; + }); + + await app.close(); + })(); + })); it('should shutdown application immediately when forceCloseConnections is true', async () => { const testingModule = await Test.createTestingModule({ @@ -168,7 +165,7 @@ describe('WebSocketGateway', () => { await app.close(); const instance = testingModule.get(ServerGateway); - expect(instance.onApplicationShutdown.called).to.be.true; + expect(instance.onApplicationShutdown).toHaveBeenCalled(); eventSource.close(); }); }); diff --git a/integration/websockets/e2e/ws-gateway.spec.ts b/integration/websockets/e2e/ws-gateway.spec.ts index 5a498ea9151..f20527e439d 100644 --- a/integration/websockets/e2e/ws-gateway.spec.ts +++ b/integration/websockets/e2e/ws-gateway.spec.ts @@ -1,14 +1,13 @@ import { INestApplication } from '@nestjs/common'; import { WsAdapter } from '@nestjs/platform-ws'; import { Test } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as WebSocket from 'ws'; -import { ApplicationGateway } from '../src/app.gateway'; -import { CoreGateway } from '../src/core.gateway'; -import { ExamplePathGateway } from '../src/example-path.gateway'; -import { ServerGateway } from '../src/server.gateway'; -import { WsPathGateway } from '../src/ws-path.gateway'; -import { WsPathGateway2 } from '../src/ws-path2.gateway'; +import WebSocket from 'ws'; +import { ApplicationGateway } from '../src/app.gateway.js'; +import { CoreGateway } from '../src/core.gateway.js'; +import { ExamplePathGateway } from '../src/example-path.gateway.js'; +import { ServerGateway } from '../src/server.gateway.js'; +import { WsPathGateway } from '../src/ws-path.gateway.js'; +import { WsPathGateway2 } from '../src/ws-path2.gateway.js'; async function createNestApp(...gateways: any[]): Promise { const testingModule = await Test.createTestingModule({ @@ -39,7 +38,7 @@ describe('WebSocketGateway (WsAdapter)', () => { ); await new Promise(resolve => ws.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws.close(); resolve(); }), @@ -63,7 +62,7 @@ describe('WebSocketGateway (WsAdapter)', () => { ); await new Promise(resolve => ws.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws.close(); resolve(); }), @@ -73,36 +72,31 @@ describe('WebSocketGateway (WsAdapter)', () => { it(`should handle message on a different path`, async () => { app = await createNestApp(WsPathGateway); await app.listen(3000); - try { - ws = new WebSocket('ws://localhost:3000/ws-path'); - await new Promise((resolve, reject) => { - ws.on('open', resolve); - ws.on('error', reject); - }); - ws.send( - JSON.stringify({ - event: 'push', - data: { - test: 'test', - }, - }), - ); - await new Promise(resolve => - ws.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); - ws.close(); - resolve(); - }), - ); - } catch (err) { - console.log(err); - } + ws = new WebSocket('ws://localhost:3000/ws-path'); + await new Promise((resolve, reject) => { + ws.on('open', resolve); + ws.on('error', reject); + }); + + ws.send( + JSON.stringify({ + event: 'push', + data: { + test: 'test', + }, + }), + ); + await new Promise(resolve => + ws.on('message', data => { + expect(JSON.parse(data.toString()).data.test).toEqual('test'); + ws.close(); + resolve(); + }), + ); }); it(`should support 2 different gateways running on different paths`, async function () { - this.retries(10); - app = await createNestApp(ExamplePathGateway, WsPathGateway2); await app.listen(3000); @@ -115,7 +109,7 @@ describe('WebSocketGateway (WsAdapter)', () => { await new Promise(resolve => ws.on('open', () => { ws.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws.close(); resolve(); }); @@ -132,7 +126,7 @@ describe('WebSocketGateway (WsAdapter)', () => { await new Promise(resolve => { ws2.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws2.close(); resolve(); }); @@ -145,11 +139,9 @@ describe('WebSocketGateway (WsAdapter)', () => { }), ); }); - }).timeout(5000); + }); it(`should support 2 different gateways running on the same path (but different ports)`, async function () { - this.retries(10); - app = await createNestApp(ApplicationGateway, CoreGateway); await app.listen(3000); @@ -162,7 +154,7 @@ describe('WebSocketGateway (WsAdapter)', () => { await new Promise(resolve => ws.on('open', () => { ws.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws.close(); resolve(); }); @@ -179,7 +171,7 @@ describe('WebSocketGateway (WsAdapter)', () => { await new Promise(resolve => { ws2.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws2.close(); resolve(); }); @@ -211,7 +203,7 @@ describe('WebSocketGateway (WsAdapter)', () => { ); await new Promise(resolve => ws.on('message', data => { - expect(JSON.parse(data.toString()).data.path).to.be.eql('getClient'); + expect(JSON.parse(data.toString()).data.path).toEqual('getClient'); ws.close(); resolve(); }), @@ -238,7 +230,7 @@ describe('WebSocketGateway (WsAdapter)', () => { ws.send(JSON.stringify(['push', { test: 'test' }])); await new Promise(resolve => ws.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws.close(); resolve(); }), @@ -266,7 +258,7 @@ describe('WebSocketGateway (WsAdapter)', () => { ws.send(JSON.stringify(['push', { test: 'test' }])); await new Promise(resolve => ws.on('message', data => { - expect(JSON.parse(data.toString()).data.test).to.be.eql('test'); + expect(JSON.parse(data.toString()).data.test).toEqual('test'); ws.close(); resolve(); }), diff --git a/integration/websockets/src/app.gateway.ts b/integration/websockets/src/app.gateway.ts index 30ea2ee22ef..c456da1b889 100644 --- a/integration/websockets/src/app.gateway.ts +++ b/integration/websockets/src/app.gateway.ts @@ -5,9 +5,9 @@ import { WebSocketGateway, WsException, } from '@nestjs/websockets'; -import { RequestInterceptor } from './request.interceptor'; +import { RequestInterceptor } from './request.interceptor.js'; import { throwError } from 'rxjs'; -import { RequestFilter } from './request.filter'; +import { RequestFilter } from './request.filter.js'; @WebSocketGateway(8080) export class ApplicationGateway { diff --git a/integration/websockets/src/app.module.ts b/integration/websockets/src/app.module.ts index 5858472b662..4982d663d12 100644 --- a/integration/websockets/src/app.module.ts +++ b/integration/websockets/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { ApplicationGateway } from './app.gateway'; +import { ApplicationGateway } from './app.gateway.js'; @Module({ providers: [ApplicationGateway], diff --git a/integration/websockets/src/server.gateway.ts b/integration/websockets/src/server.gateway.ts index 6955657cace..e12cfa6ac65 100644 --- a/integration/websockets/src/server.gateway.ts +++ b/integration/websockets/src/server.gateway.ts @@ -1,7 +1,6 @@ import { OnApplicationShutdown, UseInterceptors } from '@nestjs/common'; import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'; -import * as Sinon from 'sinon'; -import { RequestInterceptor } from './request.interceptor'; +import { RequestInterceptor } from './request.interceptor.js'; @WebSocketGateway() export class ServerGateway implements OnApplicationShutdown { @@ -22,5 +21,5 @@ export class ServerGateway implements OnApplicationShutdown { }; } - onApplicationShutdown = Sinon.spy(); + onApplicationShutdown = vi.fn(); } diff --git a/integration/websockets/tsconfig.json b/integration/websockets/tsconfig.json index 4a0f01e0ec4..aa1164082cc 100644 --- a/integration/websockets/tsconfig.json +++ b/integration/websockets/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { + "types": ["vitest/globals"], "module": "commonjs", "declaration": false, "noImplicitAny": false, "removeComments": true, "noLib": false, + "esModuleInterop": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": true, "strictNullChecks": true, diff --git a/lerna.json b/lerna.json index 12e9d0f146c..07b646f6648 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,7 @@ { - "packages": ["packages/*"], - "version": "11.1.13", + "packages": [ + "packages/*" + ], + "version": "12.0.0-alpha.1", "$schema": "node_modules/lerna/schemas/lerna-schema.json" -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d04a6f5b7d2..1e5de2bde62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "11.1.10", "hasInstallScript": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { "@nuxt/opencollective": "0.4.1", "ansis": "4.2.0", @@ -35,8 +38,6 @@ "@as-integrations/express5": "1.1.2", "@commitlint/cli": "20.4.1", "@commitlint/config-angular": "20.4.1", - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@fastify/cors": "11.2.0", "@fastify/formbody": "8.0.2", "@fastify/middie": "9.1.0", @@ -45,44 +46,33 @@ "@fastify/view": "11.1.1", "@grpc/grpc-js": "1.14.3", "@grpc/proto-loader": "0.8.0", + "@nats-io/transport-node": "^3.0.2", "@nestjs/apollo": "13.2.4", "@nestjs/graphql": "13.2.4", "@nestjs/mongoose": "11.0.4", "@nestjs/typeorm": "11.0.0", "@types/amqplib": "0.10.8", - "@types/chai": "4.3.20", - "@types/chai-as-promised": "7.1.8", "@types/cors": "2.8.19", - "@types/eslint__js": "8.42.3", "@types/express": "5.0.6", "@types/gulp": "4.0.18", "@types/http-errors": "2.0.5", - "@types/mocha": "10.0.10", - "@types/node": "25.2.3", - "@types/sinon": "21.0.0", + "@types/node": "25.2.1", "@types/supertest": "6.0.3", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.55.0", - "@typescript-eslint/parser": "8.55.0", + "@vitest/coverage-istanbul": "4.1.2", + "@vitest/coverage-v8": "4.1.2", "amqp-connection-manager": "5.0.0", "amqplib": "0.10.9", "body-parser": "2.2.2", "cache-manager": "7.2.8", - "chai": "4.5.0", - "chai-as-promised": "7.1.2", - "concurrently": "9.2.1", "conventional-changelog": "7.1.1", "coveralls": "3.1.1", "delete-empty": "3.0.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.5", "eventsource": "4.1.0", "fancy-log": "2.0.0", "fastify": "5.7.4", "fastify-plugin": "5.1.0", "find-my-way": "9.4.0", - "globals": "17.3.0", "graphql": "16.12.0", "graphql-subscriptions": "3.0.0", "gulp": "5.0.1", @@ -100,26 +90,24 @@ "light-my-request": "6.6.0", "lint-staged": "16.2.7", "markdown-table": "2.0.0", - "mocha": "11.7.5", - "mongoose": "9.2.1", + "mongoose": "9.1.6", "mqtt": "5.15.0", "multer": "2.0.2", "mysql2": "3.17.1", - "nats": "2.29.3", - "nyc": "14.1.1", - "prettier": "^3.7.4", + "oxlint": "1.58.0", + "prettier": "3.7.4", "redis": "5.10.0", "reusify": "1.1.0", "rxjs-compat": "6.6.7", - "sinon": "21.0.1", - "sinon-chai": "3.7.0", "socket.io-client": "4.8.3", "supertest": "7.2.2", "ts-morph": "27.0.2", "ts-node": "10.9.2", + "tsx": "4.19.2", "typeorm": "0.3.28", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0", + "typescript": "6.0.2", + "vite": "8.0.3", + "vitest": "4.1.2", "ws": "8.19.0" }, "engines": { @@ -135,6 +123,7 @@ "resolved": "https://registry.npmjs.org/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz", "integrity": "sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==", "dev": true, + "license": "MIT", "peerDependencies": { "graphql": "14.x || 15.x || 16.x" } @@ -145,6 +134,7 @@ "integrity": "sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==", "dev": true, "hasInstallScript": true, + "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -233,16 +223,6 @@ "@apollo/server": "^4.0.0" } }, - "node_modules/@apollo/server/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@apollo/server/node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -262,6 +242,7 @@ "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz", "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==", "dev": true, + "license": "MIT", "dependencies": { "@apollo/protobufjs": "1.2.7" } @@ -285,6 +266,7 @@ "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz", "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -341,6 +323,7 @@ "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz", "integrity": "sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -353,6 +336,7 @@ "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-2.0.1.tgz", "integrity": "sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -365,6 +349,7 @@ "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-2.0.1.tgz", "integrity": "sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==", "dev": true, + "license": "MIT", "dependencies": { "lodash.sortby": "^4.7.0" }, @@ -380,6 +365,7 @@ "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-2.0.1.tgz", "integrity": "sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -392,6 +378,7 @@ "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-2.1.0.tgz", "integrity": "sha512-LPSlBrn+S17oBy5eWkrRSGb98sWmnEzo3DPTZgp8IQc8sJe0prDgDuppGq4NeQlpoqEHz0hQeYHAOA0Z3aQsxQ==", "dev": true, + "license": "MIT", "dependencies": { "@apollo/usage-reporting-protobuf": "^4.1.0", "@apollo/utils.dropunuseddefinitions": "^2.0.1", @@ -442,43 +429,172 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/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==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-string-parser": { @@ -492,23 +608,47 @@ } }, "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -518,9 +658,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", - "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "dev": true, "license": "MIT", "engines": { @@ -528,87 +668,67 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, "node_modules/@borewit/text-codec": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", - "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", "license": "MIT", "funding": { "type": "github", @@ -616,24 +736,14 @@ } }, "node_modules/@cacheable/utils": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.3.tgz", - "integrity": "sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "hashery": "^1.3.0", - "keyv": "^5.5.5" - } - }, - "node_modules/@cacheable/utils/node_modules/keyv": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.5.tgz", - "integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==", "dev": true, "license": "MIT", "dependencies": { - "@keyv/serialize": "^1.1.1" + "hashery": "^1.5.1", + "keyv": "^5.6.0" } }, "node_modules/@commitlint/cli": { @@ -682,13 +792,13 @@ } }, "node_modules/@commitlint/config-validator": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.4.0.tgz", - "integrity": "sha512-zShmKTF+sqyNOfAE0vKcqnpvVpG0YX8F9G/ZIQHI2CoKyK+PSdladXMSns400aZ5/QZs+0fN75B//3Q5CHw++w==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.5.0.tgz", + "integrity": "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "ajv": "^8.11.0" }, "engines": { @@ -696,13 +806,13 @@ } }, "node_modules/@commitlint/ensure": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.4.1.tgz", - "integrity": "sha512-WLQqaFx1pBooiVvBrA1YfJNFqZF8wS/YGOtr5RzApDbV9tQ52qT5VkTsY65hFTnXhW8PcDfZLaknfJTmPejmlw==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.0.tgz", + "integrity": "sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -724,13 +834,13 @@ } }, "node_modules/@commitlint/format": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.4.0.tgz", - "integrity": "sha512-i3ki3WR0rgolFVX6r64poBHXM1t8qlFel1G1eCBvVgntE3fCJitmzSvH5JD/KVJN/snz6TfaX2CLdON7+s4WVQ==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.5.0.tgz", + "integrity": "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "picocolors": "^1.1.1" }, "engines": { @@ -738,60 +848,47 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.4.1.tgz", - "integrity": "sha512-In5EO4JR1lNsAv1oOBBO24V9ND1IqdAJDKZiEpdfjDl2HMasAcT7oA+5BKONv1pRoLG380DGPE2W2RIcUwdgLA==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.5.0.tgz", + "integrity": "sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "semver": "^7.6.0" }, "engines": { "node": ">=v18" } }, - "node_modules/@commitlint/is-ignored/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@commitlint/lint": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.4.1.tgz", - "integrity": "sha512-g94LrGl/c6UhuhDQqNqU232aslLEN2vzc7MPfQTHzwzM4GHNnEAwVWWnh0zX8S5YXecuLXDwbCsoGwmpAgPWKA==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.0.tgz", + "integrity": "sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/is-ignored": "^20.4.1", - "@commitlint/parse": "^20.4.1", - "@commitlint/rules": "^20.4.1", - "@commitlint/types": "^20.4.0" + "@commitlint/is-ignored": "^20.5.0", + "@commitlint/parse": "^20.5.0", + "@commitlint/rules": "^20.5.0", + "@commitlint/types": "^20.5.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/load": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.4.0.tgz", - "integrity": "sha512-Dauup/GfjwffBXRJUdlX/YRKfSVXsXZLnINXKz0VZkXdKDcaEILAi9oflHGbfydonJnJAbXEbF3nXPm9rm3G6A==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.0.tgz", + "integrity": "sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^20.4.0", + "@commitlint/config-validator": "^20.5.0", "@commitlint/execute-rule": "^20.0.0", - "@commitlint/resolve-extends": "^20.4.0", - "@commitlint/types": "^20.4.0", - "cosmiconfig": "^9.0.0", + "@commitlint/resolve-extends": "^20.5.0", + "@commitlint/types": "^20.5.0", + "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "is-plain-obj": "^4.1.0", "lodash.mergewith": "^4.6.2", @@ -801,23 +898,10 @@ "node": ">=v18" } }, - "node_modules/@commitlint/load/node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@commitlint/message": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.0.tgz", - "integrity": "sha512-B5lGtvHgiLAIsK5nLINzVW0bN5hXv+EW35sKhYHE8F7V9Uz1fR4tx3wt7mobA5UNhZKUNgB/+ldVMQE6IHZRyA==", + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz", + "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==", "dev": true, "license": "MIT", "engines": { @@ -825,43 +909,30 @@ } }, "node_modules/@commitlint/parse": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.4.1.tgz", - "integrity": "sha512-XNtZjeRcFuAfUnhYrCY02+mpxwY4OmnvD3ETbVPs25xJFFz1nRo/25nHj+5eM+zTeRFvWFwD4GXWU2JEtoK1/w==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.5.0.tgz", + "integrity": "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", - "conventional-changelog-angular": "^8.1.0", - "conventional-commits-parser": "^6.2.1" + "@commitlint/types": "^20.5.0", + "conventional-changelog-angular": "^8.2.0", + "conventional-commits-parser": "^6.3.0" }, "engines": { "node": ">=v18" } }, - "node_modules/@commitlint/parse/node_modules/conventional-changelog-angular": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.1.0.tgz", - "integrity": "sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@commitlint/read": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.4.0.tgz", - "integrity": "sha512-QfpFn6/I240ySEGv7YWqho4vxqtPpx40FS7kZZDjUJ+eHxu3azfhy7fFb5XzfTqVNp1hNoI3tEmiEPbDB44+cg==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.5.0.tgz", + "integrity": "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/top-level": "^20.4.0", - "@commitlint/types": "^20.4.0", - "git-raw-commits": "^4.0.0", + "@commitlint/top-level": "^20.4.3", + "@commitlint/types": "^20.5.0", + "git-raw-commits": "^5.0.0", "minimist": "^1.2.8", "tinyexec": "^1.0.0" }, @@ -870,14 +941,14 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.4.0.tgz", - "integrity": "sha512-ay1KM8q0t+/OnlpqXJ+7gEFQNlUtSU5Gxr8GEwnVf2TPN3+ywc5DzL3JCxmpucqxfHBTFwfRMXxPRRnR5Ki20g==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.0.tgz", + "integrity": "sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^20.4.0", - "@commitlint/types": "^20.4.0", + "@commitlint/config-validator": "^20.5.0", + "@commitlint/types": "^20.5.0", "global-directory": "^4.0.1", "import-meta-resolve": "^4.0.0", "lodash.mergewith": "^4.6.2", @@ -888,16 +959,16 @@ } }, "node_modules/@commitlint/rules": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.4.1.tgz", - "integrity": "sha512-WtqypKEPbQEuJwJS4aKs0OoJRBKz1HXPBC9wRtzVNH68FLhPWzxXlF09hpUXM9zdYTpm4vAdoTGkWiBgQ/vL0g==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.0.tgz", + "integrity": "sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/ensure": "^20.4.1", - "@commitlint/message": "^20.4.0", + "@commitlint/ensure": "^20.5.0", + "@commitlint/message": "^20.4.3", "@commitlint/to-lines": "^20.0.0", - "@commitlint/types": "^20.4.0" + "@commitlint/types": "^20.5.0" }, "engines": { "node": ">=v18" @@ -914,9 +985,9 @@ } }, "node_modules/@commitlint/top-level": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.0.tgz", - "integrity": "sha512-NDzq8Q6jmFaIIBC/GG6n1OQEaHdmaAAYdrZRlMgW6glYWGZ+IeuXmiymDvQNXPc82mVxq2KiE3RVpcs+1OeDeA==", + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz", + "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==", "dev": true, "license": "MIT", "dependencies": { @@ -927,13 +998,13 @@ } }, "node_modules/@commitlint/types": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.4.0.tgz", - "integrity": "sha512-aO5l99BQJ0X34ft8b0h7QFkQlqxC6e7ZPVmBKz13xM9O8obDaM1Cld4sQlJDXXU/VFuUzQ30mVtHjVz74TuStw==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.5.0.tgz", + "integrity": "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^6.2.1", + "conventional-commits-parser": "^6.3.0", "picocolors": "^1.1.1" }, "engines": { @@ -941,14 +1012,14 @@ } }, "node_modules/@conventional-changelog/git-client": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.5.1.tgz", - "integrity": "sha512-lAw7iA5oTPWOLjiweb7DlGEMDEvzqzLLa6aWOly2FSZ64IwLE8T458rC+o+WvI31Doz6joM7X2DoNog7mX8r4A==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.6.0.tgz", + "integrity": "sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==", "dev": true, "license": "MIT", "dependencies": { "@simple-libs/child-process-utils": "^1.0.0", - "@simple-libs/stream-utils": "^1.1.0", + "@simple-libs/stream-utils": "^1.2.0", "semver": "^7.5.2" }, "engines": { @@ -956,7 +1027,7 @@ }, "peerDependencies": { "conventional-commits-filter": "^5.0.0", - "conventional-commits-parser": "^6.1.0" + "conventional-commits-parser": "^6.3.0" }, "peerDependenciesMeta": { "conventional-commits-filter": { @@ -967,24 +1038,12 @@ } } }, - "node_modules/@conventional-changelog/git-client/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -992,21 +1051,32 @@ "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", "dev": true, "license": "MIT", "dependencies": { - "@emnapi/wasi-threads": "1.1.0", + "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", "dev": true, "license": "MIT", "dependencies": { @@ -1014,298 +1084,427 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/config-array/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@eslint/config-array/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "Python-2.0" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": ">=18" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@fastify/accept-negotiator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", - "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", + "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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ] - }, - "node_modules/@fastify/ajv-compiler": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", - "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==", + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/accept-negotiator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", + "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", "dev": true, "funding": [ { @@ -1317,6 +1516,22 @@ "url": "https://opencollective.com/fastify" } ], + "license": "MIT" + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", + "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { "ajv": "^8.12.0", @@ -1325,16 +1540,16 @@ } }, "node_modules/@fastify/busboy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", - "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", - "dev": true + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "dev": true, + "license": "MIT" }, "node_modules/@fastify/cors": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.2.0.tgz", "integrity": "sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==", - "dev": true, "funding": [ { "type": "github", @@ -1352,9 +1567,9 @@ } }, "node_modules/@fastify/deepmerge": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-3.1.0.tgz", - "integrity": "sha512-lCVONBQINyNhM6LLezB6+2afusgEYR4G8xenMsfe+AT+iZ7Ca6upM5Ha8UkZuYSnuMw3GWl/BiPXnLMi/gSxuQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-3.2.1.tgz", + "integrity": "sha512-N5Oqvltoa2r9z1tbx4xjky0oRR60v+T47Ic4J1ukoVQcptLOrIdRnCSdTGmOmajZuHVKlTnfcmrjyqsGEW1ztA==", "dev": true, "funding": [ { @@ -1369,16 +1584,25 @@ "license": "MIT" }, "node_modules/@fastify/error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.0.0.tgz", - "integrity": "sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==", - "dev": true + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", + "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" }, "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.2.tgz", - "integrity": "sha512-YdR7gqlLg1xZAQa+SX4sMNzQHY5pC54fu9oC5aYSUqBhyn6fkLkrdtKlpVdCNPlwuUuXA1PjFTEmvMF6ZVXVGw==", - "dev": true, + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", + "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", "funding": [ { "type": "github", @@ -1389,6 +1613,7 @@ "url": "https://opencollective.com/fastify" } ], + "license": "MIT", "dependencies": { "fast-json-stringify": "^6.0.0" } @@ -1397,7 +1622,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@fastify/formbody/-/formbody-8.0.2.tgz", "integrity": "sha512-84v5J2KrkXzjgBpYnaNRPqwgMsmY7ZDjuj0YVuMR3NXCJRCgKEZy/taSP1wUYGn0onfxJpLyRGDLa+NMaDJtnA==", - "dev": true, "funding": [ { "type": "github", @@ -1415,10 +1639,20 @@ } }, "node_modules/@fastify/forwarded": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", - "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", - "dev": true + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", + "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" }, "node_modules/@fastify/merge-json-schemas": { "version": "0.2.1", @@ -1434,6 +1668,7 @@ "url": "https://opencollective.com/fastify" } ], + "license": "MIT", "dependencies": { "dequal": "^2.0.3" } @@ -1462,21 +1697,6 @@ "reusify": "^1.0.4" } }, - "node_modules/@fastify/middie/node_modules/find-my-way": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.4.0.tgz", - "integrity": "sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^5.0.0" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/@fastify/multipart": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-9.4.0.tgz", @@ -1502,28 +1722,29 @@ } }, "node_modules/@fastify/proxy-addr": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", - "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==", - "dev": true, + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", + "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, - "node_modules/@fastify/proxy-addr/node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/@fastify/send": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.0.0.tgz", - "integrity": "sha512-eJjKDxyBnZ1iMHcmwYWG5wSA/yzVY/yrBy3Upd2+hc0omcK13tWeXRcbF28zEcbl+Z2kXEgMzJ5Rb/gXGWx9Rg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz", + "integrity": "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==", "dev": true, "funding": [ { @@ -1544,19 +1765,6 @@ "mime": "^3" } }, - "node_modules/@fastify/send/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/@fastify/static": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@fastify/static/-/static-9.0.0.tgz", @@ -1582,57 +1790,6 @@ "glob": "^13.0.0" } }, - "node_modules/@fastify/static/node_modules/glob": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", - "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@fastify/static/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@fastify/static/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@fastify/view": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/@fastify/view/-/view-11.1.1.tgz", @@ -1654,11 +1811,22 @@ "toad-cache": "^3.7.0" } }, + "node_modules/@gar/promise-retry": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", + "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@graphql-tools/merge": { "version": "9.1.7", @@ -1769,6 +1937,7 @@ "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", "dev": true, + "license": "MIT", "dependencies": { "acorn": "^6.4.1", "normalize-path": "^3.0.0", @@ -1785,6 +1954,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1792,57 +1962,12 @@ "node": ">=0.4.0" } }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.4", "readable-stream": "2 || 3" @@ -1853,6 +1978,7 @@ "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", "dev": true, + "license": "MIT", "dependencies": { "normalize-path": "^2.0.1", "through2": "^2.0.3" @@ -1866,6 +1992,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, + "license": "MIT", "dependencies": { "remove-trailing-separator": "^1.0.1" }, @@ -1896,68 +2023,6 @@ "node": ">=10.13.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", @@ -2066,21 +2131,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@inquirer/editor": { "version": "4.2.23", "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", @@ -2353,377 +2403,144 @@ "dev": true, "license": "MIT" }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": "20 || >=22" + "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==", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dev": true, + "license": "ISC", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "minipass": "^7.0.4" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } + "license": "ISC" }, - "node_modules/@isaacs/cliui/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==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=8" } }, - "node_modules/@isaacs/cliui/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/@isaacs/cliui/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==", + "node_modules/@jest/diff-sequences": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", + "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/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" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@isaacs/cliui/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==", + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@isaacs/cliui/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/@isaacs/cliui/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==", + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@isaacs/cliui/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==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@isaacs/cliui/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==", + "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, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/@isaacs/cliui/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==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "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" - } + "license": "MIT" }, - "node_modules/@isaacs/cliui/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==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "node_modules/@isaacs/cliui/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" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@isaacs/cliui/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==", + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, + "license": "MIT", "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" } }, - "node_modules/@isaacs/cliui/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/@isaacs/cliui/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/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", - "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "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/gen-mapping/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/@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.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@keyv/serialize": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", "dev": true, "license": "MIT" }, @@ -2731,6 +2548,7 @@ "version": "9.0.4", "resolved": "https://registry.npmjs.org/@lerna/create/-/create-9.0.4.tgz", "integrity": "sha512-WxedGD98G8/a6HztCXNWquaM0x17oSvfvuqDsLxNNX1qXGyrzmMUmd1mQikF/47uy80X6qyWdaRtaAHlwkvEUA==", + "deprecated": "This package is an implementation detail of Lerna and is no longer published separately.", "dev": true, "license": "MIT", "dependencies": { @@ -2806,13 +2624,6 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/@lerna/create/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/@lerna/create/node_modules/chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -2830,61 +2641,31 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@lerna/create/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@lerna/create/node_modules/glob": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.2.tgz", - "integrity": "sha512-035InabNu/c1lW0tzPhAgapKctblppqsKKG9ZaNzbr+gXwWMjXoiyGSyB9sArzrjG7jY+zntRq5ZSUYemrnWVQ==", + "node_modules/@lerna/create/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "minimatch": "^10.1.2", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, "engines": { - "node": "20 || >=22" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@lerna/create/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@lerna/create/node_modules/glob/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "url": "https://github.com/sponsors/d-fischer" }, - "engines": { - "node": "20 || >=22" + "peerDependencies": { + "typescript": ">=4.9.5" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@lerna/create/node_modules/ini": { @@ -2894,45 +2675,6 @@ "dev": true, "license": "ISC" }, - "node_modules/@lerna/create/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@lerna/create/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@lerna/create/node_modules/load-json-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", - "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^5.0.0", - "strip-bom": "^4.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@lerna/create/node_modules/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", @@ -2946,28 +2688,31 @@ "node": "*" } }, - "node_modules/@lerna/create/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "node_modules/@lerna/create/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, "engines": { - "node": ">= 18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@lerna/create/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "node_modules/@lerna/create/node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "engines": { "node": "20 || >=22" @@ -2976,48 +2721,15 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lerna/create/node_modules/pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "node_modules/@lerna/create/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@lerna/create/node_modules/rimraf": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", - "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "glob": "^13.0.0", - "package-json-from-dist": "^1.0.1" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@lerna/create/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { "node": ">=10" } @@ -3032,31 +2744,17 @@ "node": ">=8" } }, - "node_modules/@lerna/create/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "node_modules/@lerna/create/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@lerna/create/node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", - "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=8" } }, "node_modules/@lerna/create/node_modules/tinyglobby": { @@ -3076,17 +2774,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/@lerna/create/node_modules/upath": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", - "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4", - "yarn": "*" - } - }, "node_modules/@lerna/create/node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -3101,47 +2788,18 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/@lerna/create/node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@lerna/create/node_modules/write-file-atomic/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==", + "node_modules/@ltd/j-toml": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@ltd/j-toml/-/j-toml-1.38.0.tgz", + "integrity": "sha512-lYtBcmvHustHQtg4X7TXUu1Xa/tbLC3p2wLvgQI+fWVySguVZJF60Snxijw5EiohumxZbR10kWYFFebh1zotiw==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@lerna/create/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } + "license": "LGPL-3.0" }, "node_modules/@lukeed/csprng": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3157,9 +2815,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", - "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz", + "integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==", "dev": true, "license": "MIT", "dependencies": { @@ -3178,6 +2836,55 @@ "@tybys/wasm-util": "^0.9.0" } }, + "node_modules/@nats-io/nats-core": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@nats-io/nats-core/-/nats-core-3.3.1.tgz", + "integrity": "sha512-myFXGTo4cCfKrsLDjkoEz7FjjjvSfBRjun7Qx3n3Z5OzW4JUY8Ou7VQsGAdXLQxHN3ae/XNXvmXxshDoFPex4w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@nats-io/nkeys": "2.0.3", + "@nats-io/nuid": "2.0.3" + } + }, + "node_modules/@nats-io/nkeys": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nats-io/nkeys/-/nkeys-2.0.3.tgz", + "integrity": "sha512-JVt56GuE6Z89KUkI4TXUbSI9fmIfAmk6PMPknijmuL72GcD+UgIomTcRWiNvvJKxA01sBbmIPStqJs5cMRBC3A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "^1.0.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@nats-io/nuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nats-io/nuid/-/nuid-2.0.3.tgz", + "integrity": "sha512-TpA3HEBna/qMVudy+3HZr5M3mo/L1JPofpVT4t0HkFGkz2Cn9wrlrQC8tvR8Md5Oa9//GtGG26eN0qEWF5Vqew==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 18.x" + } + }, + "node_modules/@nats-io/transport-node": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@nats-io/transport-node/-/transport-node-3.3.1.tgz", + "integrity": "sha512-GBvY0VcvyQEILgy5bjpqU1GpDYmSF06bW59I7cewZuNGS9u3AoV/gf+a+3ep45T/Z+UC661atq/b7x+QV12w+Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@nats-io/nats-core": "3.3.1", + "@nats-io/nkeys": "2.0.3", + "@nats-io/nuid": "2.0.3" + }, + "engines": { + "node": ">= 18.0.0" + } + }, "node_modules/@nestjs/apollo": { "version": "13.2.4", "resolved": "https://registry.npmjs.org/@nestjs/apollo/-/apollo-13.2.4.tgz", @@ -3216,80 +2923,12 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.13", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.13.tgz", - "integrity": "sha512-ieqWtipT+VlyDWLz5Rvz0f3E5rXcVAnaAi+D53DEHLjc1kmFxCgZ62qVfTX2vwkywwqNkTNXvBgGR72hYqV//Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "file-type": "21.3.0", - "iterare": "1.2.1", - "load-esm": "1.0.3", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "class-transformer": ">=0.4.1", - "class-validator": ">=0.13.2", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } + "resolved": "packages/common", + "link": true }, "node_modules/@nestjs/core": { - "version": "11.1.13", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.13.tgz", - "integrity": "sha512-Tq9EIKiC30EBL8hLK93tNqaToy0hzbuVGYt29V8NhkVJUsDzlmiVf6c3hSPtzx2krIUVbTgQ2KFeaxr72rEyzQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@nuxt/opencollective": "0.4.1", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "path-to-regexp": "8.3.0", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "engines": { - "node": ">= 20" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/microservices": "^11.0.0", - "@nestjs/platform-express": "^11.0.0", - "@nestjs/websockets": "^11.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - }, - "@nestjs/websockets": { - "optional": true - } - } + "resolved": "packages/core", + "link": true }, "node_modules/@nestjs/graphql": { "version": "13.2.4", @@ -3358,6 +2997,10 @@ } } }, + "node_modules/@nestjs/microservices": { + "resolved": "packages/microservices", + "link": true + }, "node_modules/@nestjs/mongoose": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-11.0.4.tgz", @@ -3371,6 +3014,26 @@ "rxjs": "^7.0.0" } }, + "node_modules/@nestjs/platform-express": { + "resolved": "packages/platform-express", + "link": true + }, + "node_modules/@nestjs/platform-fastify": { + "resolved": "packages/platform-fastify", + "link": true + }, + "node_modules/@nestjs/platform-socket.io": { + "resolved": "packages/platform-socket.io", + "link": true + }, + "node_modules/@nestjs/platform-ws": { + "resolved": "packages/platform-ws", + "link": true + }, + "node_modules/@nestjs/testing": { + "resolved": "packages/testing", + "link": true + }, "node_modules/@nestjs/typeorm": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.0.tgz", @@ -3385,6 +3048,10 @@ "typeorm": "^0.3.0" } }, + "node_modules/@nestjs/websockets": { + "resolved": "packages/websockets", + "link": true + }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -3403,6 +3070,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3416,6 +3084,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -3425,6 +3094,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -3450,55 +3120,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@npmcli/agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@npmcli/agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/@npmcli/arborist": { "version": "9.1.6", "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-9.1.6.tgz", @@ -3547,58 +3168,45 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/arborist/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "node_modules/@npmcli/arborist/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" } }, - "node_modules/@npmcli/arborist/node_modules/hosted-git-info": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", - "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "node_modules/@npmcli/arborist/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "lru-cache": "^11.1.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "18 || 20 || >=22" } }, "node_modules/@npmcli/arborist/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/arborist/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/@npmcli/arborist/node_modules/npm-bundled": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", @@ -3623,12 +3231,13 @@ } }, "node_modules/@npmcli/arborist/node_modules/pacote": { - "version": "21.0.4", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.4.tgz", - "integrity": "sha512-RplP/pDW0NNNDh3pnaoIWYPvNenS7UqMbXyvMqJczosiFWTeGGwJC2NQBLqKf4rGLFfwCOnntw1aEp9Jiqm1MA==", + "version": "21.5.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.5.0.tgz", + "integrity": "sha512-VtZ0SB8mb5Tzw3dXDfVAIjhyVKUHZkS/ZH9/5mpKenwC9sFOXNI0JI7kEF7IMkwOnsWMFrvAZHzx1T5fmrp9FQ==", "dev": true, "license": "ISC", "dependencies": { + "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", @@ -3642,7 +3251,6 @@ "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" @@ -3682,9 +3290,9 @@ } }, "node_modules/@npmcli/arborist/node_modules/pacote/node_modules/ssri": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", - "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", "dev": true, "license": "ISC", "dependencies": { @@ -3694,46 +3302,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/arborist/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/arborist/node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@npmcli/arborist/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@npmcli/fs": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", @@ -3747,32 +3315,19 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@npmcli/git": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.1.tgz", - "integrity": "sha512-+XTFxK2jJF/EJJ5SoAzXk3qwIDfvFc5/g+bD274LZ7uY7LE8sTfG6Z8rOanPl2ZEvZWqNvmEdtXC25cE54VcoA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", "dev": true, "license": "ISC", "dependencies": { + "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^6.0.0" }, @@ -3791,13 +3346,13 @@ } }, "node_modules/@npmcli/git/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/@npmcli/git/node_modules/proc-log": { @@ -3810,27 +3365,14 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/git/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@npmcli/git/node_modules/which": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", - "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" + "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" @@ -3882,52 +3424,40 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/map-workspaces/node_modules/glob": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", - "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "node_modules/@npmcli/map-workspaces/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "18 || 20 || >=22" } }, - "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "18 || 20 || >=22" } }, - "node_modules/@npmcli/map-workspaces/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3950,16 +3480,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/metavuln-calculator/node_modules/json-parse-even-better-errors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", - "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/metavuln-calculator/node_modules/proc-log": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", @@ -3970,25 +3490,13 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/metavuln-calculator/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@npmcli/move-file": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, + "license": "MIT", "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -3997,11 +3505,34 @@ "node": ">=10" } }, + "node_modules/@npmcli/move-file/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@npmcli/move-file/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -4015,6 +3546,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -4064,10 +3596,34 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@npmcli/package-json/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@npmcli/package-json/node_modules/glob": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -4088,73 +3644,17 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/package-json/node_modules/hosted-git-info": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", - "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@npmcli/package-json/node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/package-json/node_modules/json-parse-even-better-errors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", - "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@npmcli/package-json/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/package-json/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4170,19 +3670,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/package-json/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", @@ -4197,23 +3684,23 @@ } }, "node_modules/@npmcli/promise-spawn/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", - "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" + "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" @@ -4325,26 +3812,17 @@ "npm": ">=5.10.0" } }, - "node_modules/@nuxt/opencollective/node_modules/consola": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.3.3.tgz", - "integrity": "sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, "node_modules/@nx/devkit": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-22.3.3.tgz", - "integrity": "sha512-/hxcdhE+QDalsWEbJurHtZh9aY27taHeImbCVJnogwv85H3RbAE+0YuKXGInutfLszAs7phwzli71yq+d2P45Q==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-22.6.3.tgz", + "integrity": "sha512-GUGQGU1XcNHLQcUEq/JqNqTGikfdJQAgiyauwKr5z2dUNWK+OmUJE9J0tqANbPBZO5wtwMpRNXtVWtxQqgX8nQ==", "dev": true, "license": "MIT", "dependencies": { "@zkochan/js-yaml": "0.0.7", "ejs": "^3.1.7", "enquirer": "~2.3.6", - "minimatch": "9.0.3", + "minimatch": "10.2.4", "semver": "^7.6.3", "tslib": "^2.3.0", "yargs-parser": "21.1.1" @@ -4353,49 +3831,49 @@ "nx": ">= 21 <= 23 || ^22.0.0-0" } }, + "node_modules/@nx/devkit/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@nx/devkit/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@nx/devkit/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nx/devkit/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@nx/nx-darwin-arm64": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-22.3.3.tgz", - "integrity": "sha512-zBAGFGLal09CxhQkdMpOVwcwa9Y01aFm88jTTn35s/DdIWsfngmPzz0t4mG7u2D05q7TJfGQ31pIf5GkNUjo6g==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-22.6.3.tgz", + "integrity": "sha512-m8hEp2WufqUJzrl2uI5OItkPqIo8+0lbOBEKI7yZN9uoL6FKzP5LF6WlMFPJ8FlajtjBzQqaoDwp04+bkuXeaw==", "cpu": [ "arm64" ], @@ -4407,9 +3885,9 @@ ] }, "node_modules/@nx/nx-darwin-x64": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-22.3.3.tgz", - "integrity": "sha512-6ZQ6rMqH8NY4Jz+Gc89D5bIH2NxZb5S/vaA4yJ9RrqAfl4QWchNFD5na+aRivSd+UdsYLPKKl6qohet5SE6vOg==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-22.6.3.tgz", + "integrity": "sha512-biPybnU2qlNuP7ytBYmRuusrU5TWXqVKMHr7Kxrqlin87iJR5MosXSZ+Pjr8H+0zFrB4rGf/9yro3s/dYG40Yw==", "cpu": [ "x64" ], @@ -4421,9 +3899,9 @@ ] }, "node_modules/@nx/nx-freebsd-x64": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-22.3.3.tgz", - "integrity": "sha512-J/PP5pIOQtR7ZzrFwP6d6h0yfY7r9EravG2m940GsgzGbtZGYIDqnh5Wdt+4uBWPH8VpdNOwFqH0afELtJA3MA==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-22.6.3.tgz", + "integrity": "sha512-8C6hhvVuqPwnvjHMPAA77DeEZ/WSY6AxuuIiyRje9uKF2B5F26sV89lRjBoEiWnV1dmLdy5YY5HJZEjwqjifAQ==", "cpu": [ "x64" ], @@ -4435,9 +3913,9 @@ ] }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-22.3.3.tgz", - "integrity": "sha512-/zn0altzM15S7qAgXMaB41vHkEn18HyTVUvRrjmmwaVqk9WfmDmqOQlGWoJ6XCbpvKQ8bh14RyhR9LGw1JJkNA==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-22.6.3.tgz", + "integrity": "sha512-8gWDhe4lY3pegmKx5/z7z/h4adlmL+3wuPXMUlBtMkhJ5TX1z94PkVtHRprEsHuQHO7PsSFaOJdsIZbr/sx7SQ==", "cpu": [ "arm" ], @@ -4449,9 +3927,9 @@ ] }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-22.3.3.tgz", - "integrity": "sha512-NmPeCexWIZHW9RM3lDdFENN9C3WtlQ5L4RSNFESIjreS921rgePhulsszYdGnHdcnKPYlBBJnX/NxVsfioBbnQ==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-22.6.3.tgz", + "integrity": "sha512-ZRP5qf4lsk0HFuvhhSJc+t3a0NKc+WXElKPXTEK9DGOluY327lUogeZrSSJfxGf+dBTtpuRIO8rOIrnZOf5Xww==", "cpu": [ "arm64" ], @@ -4463,9 +3941,9 @@ ] }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-22.3.3.tgz", - "integrity": "sha512-K02U88Q0dpvCfmSXXvY7KbYQSa1m+mkYeqDBRHp11yHk1GoIqaHp8oEWda7FV4gsriNExPSS5tX1/QGVoLZrCw==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-22.6.3.tgz", + "integrity": "sha512-AcOf/5UJD7Fyc2ujHYajxLw+ajJ8C1IhHoCQyLwBpd/15lu3pii9Z9G4cNBm0ejKnnzofzRmhv2xka9qqCtpXQ==", "cpu": [ "arm64" ], @@ -4477,9 +3955,9 @@ ] }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-22.3.3.tgz", - "integrity": "sha512-04TEbvgwRaB9ifr39YwJmWh3RuXb4Ry4m84SOJyjNXAfPrepcWgfIQn1VL2ul1Ybq+P023dLO9ME8uqFh6j1YQ==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-22.6.3.tgz", + "integrity": "sha512-KxSdUCGOt2GGXzgggp9sSLJacWj7AAI410UPOEGw5F6GS5148e+kiy3piULF/0NE5/q40IK7gyS43HY99qgAqQ==", "cpu": [ "x64" ], @@ -4491,9 +3969,9 @@ ] }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-22.3.3.tgz", - "integrity": "sha512-uxBXx5q+S5OGatbYDxnamsKXRKlYn+Eq1nrCAHaf8rIfRoHlDiRV2PqtWuF+O2pxR5FWKpvr+/sZtt9rAf7KMw==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-22.6.3.tgz", + "integrity": "sha512-Tvlw6XvTj+5IQRkprV3AdCKnlQFYh2OJYn0wgHrvQWeV1Eks/RaCoRChfHXdAyE4S64YrBA6NAOxfXANh3yLTg==", "cpu": [ "x64" ], @@ -4505,9 +3983,9 @@ ] }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-22.3.3.tgz", - "integrity": "sha512-aOwlfD6ZA1K6hjZtbhBSp7s1yi3sHbMpLCa4stXzfhCCpKUv46HU/EdiWdE1N8AsyNFemPZFq81k1VTowcACdg==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-22.6.3.tgz", + "integrity": "sha512-9yRRuoVeQdV52GJtHo+vH6+es2PNF8skWlUa74jyWRsoZM9Ew8JmRZruRfhkUmhjJTrguqJLj9koa/NXgS0yeg==", "cpu": [ "arm64" ], @@ -4519,9 +3997,9 @@ ] }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-22.3.3.tgz", - "integrity": "sha512-EDR8BtqeDvVNQ+kPwnfeSfmerYetitU3tDkxOMIybjKJDh69U2JwTB8n9ARwNaZQbNk7sCGNRUSZFTbAAUKvuQ==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-22.6.3.tgz", + "integrity": "sha512-21wjiUSV5hMa1oj8UfpfMTxpROksWrr/minAv8ejmGFwUSoztSzAkNf5i4PESPsbYNytjKooDzzAiQMLo6b0kg==", "cpu": [ "x64" ], @@ -4706,4305 +4184,4131 @@ "@octokit/openapi-types": "^24.2.0" } }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", "dev": true, "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@pinojs/redact": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", - "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.58.0.tgz", + "integrity": "sha512-1T7UN3SsWWxpWyWGn1cT3ASNJOo+pI3eUkmEl7HgtowapcV8kslYpFQcYn431VuxghXakPNlbjRwhqmR37PFOg==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.58.0.tgz", + "integrity": "sha512-GryzujxuiRv2YFF7bRy8mKcxlbuAN+euVUtGJt9KKbLT8JBUIosamVhcthLh+VEr6KE6cjeVMAQxKAzJcoN7dg==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=14" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.58.0.tgz", + "integrity": "sha512-7/bRSJIwl4GxeZL9rPZ11anNTyUO9epZrfEJH/ZMla3+/gbQ6xZixh9nOhsZ0QwsTW7/5J2A/fHbD1udC5DQQA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "dev": true - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "dev": true - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.58.0.tgz", + "integrity": "sha512-EqdtJSiHweS2vfILNrpyJ6HUwpEq2g7+4Zx1FPi4hu3Hu7tC3znF6ufbXO8Ub2LD4mGgznjI7kSdku9NDD1Mkg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "dev": true - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "dev": true - }, - "node_modules/@redis/bloom": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.10.0.tgz", - "integrity": "sha512-doIF37ob+l47n0rkpRNgU8n4iacBlKM9xLiP1LtTZTvz8TloJB8qx/MgvhMhKdYG+CvCY2aPBnN2706izFn/4A==", + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.58.0.tgz", + "integrity": "sha512-VQt5TH4M42mY20F545G637RKxV/yjwVtKk2vfXuazfReSIiuvWBnv+FVSvIV5fKVTJNjt3GSJibh6JecbhGdBw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@redis/client": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.10.0.tgz", - "integrity": "sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==", + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.58.0.tgz", + "integrity": "sha512-fBYcj4ucwpAtjJT3oeBdFBYKvNyjRSK+cyuvBOTQjh0jvKp4yeA4S/D0IsCHus/VPaNG5L48qQkh+Vjy3HL2/Q==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@redis/json": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.10.0.tgz", - "integrity": "sha512-B2G8XlOmTPUuZtD44EMGbtoepQG34RCDXLZbjrtON1Djet0t5Ri7/YPXvL9aomXqP8lLTreaprtyLKF4tmXEEA==", + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.58.0.tgz", + "integrity": "sha512-0BeuFfwlUHlJ1xpEdSD1YO3vByEFGPg36uLjK1JgFaxFb4W6w17F8ET8sz5cheZ4+x5f2xzdnRrrWv83E3Yd8g==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@redis/search": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.10.0.tgz", - "integrity": "sha512-3SVcPswoSfp2HnmWbAGUzlbUPn7fOohVu2weUQ0S+EMiQi8jwjL+aN2p6V3TI65eNfVsJ8vyPvqWklm6H6esmg==", + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.58.0.tgz", + "integrity": "sha512-TXlZgnPTlxrQzxG9ZXU7BNwx1Ilrr17P3GwZY0If2EzrinqRH3zXPc3HrRcBJgcsoZNMuNL5YivtkJYgp467UQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@redis/time-series": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.10.0.tgz", - "integrity": "sha512-cPkpddXH5kc/SdRhF0YG0qtjL+noqFT0AcHbQ6axhsPsO7iqPi1cjxgdkE9TNeKiBUUdCaU1DbqkR/LzbzPBhg==", + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.58.0.tgz", + "integrity": "sha512-zSoYRo5dxHLcUx93Stl2hW3hSNjPt99O70eRVWt5A1zwJ+FPjeCCANCD2a9R4JbHsdcl11TIQOjyigcRVOH2mw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/bundle": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", - "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.58.0.tgz", + "integrity": "sha512-NQ0U/lqxH2/VxBYeAIvMNUK1y0a1bJ3ZicqkF2c6wfakbEciP9jvIE4yNzCFpZaqeIeRYaV7AVGqEO1yrfVPjA==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/core": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.1.0.tgz", - "integrity": "sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A==", + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.58.0.tgz", + "integrity": "sha512-X9J+kr3gIC9FT8GuZt0ekzpNUtkBVzMVU4KiKDSlocyQuEgi3gBbXYN8UkQiV77FTusLDPsovjo95YedHr+3yg==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/protobuf-specs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz", - "integrity": "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==", + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.58.0.tgz", + "integrity": "sha512-CDze3pi1OO3Wvb/QsXjmLEY4XPKGM6kIo82ssNOgmcl1IdndF9VSGAE38YLhADWmOac7fjqhBw82LozuUVxD0Q==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.0.tgz", - "integrity": "sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg==", + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.58.0.tgz", + "integrity": "sha512-b/89glbxFaEAcA6Uf1FvCNecBJEgcUTsV1quzrqXM/o4R1M4u+2KCVuyGCayN2UpsRWtGGLb+Ver0tBBpxaPog==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.1.0", - "@sigstore/protobuf-specs": "^0.5.0", - "make-fetch-happen": "^15.0.3", - "proc-log": "^6.1.0", - "promise-retry": "^2.0.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", - "integrity": "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^4.0.0", - "cacache": "^20.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^5.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", - "ssri": "^13.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@sigstore/sign/node_modules/minipass-fetch": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.0.tgz", - "integrity": "sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==", + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.58.0.tgz", + "integrity": "sha512-0/yYpkq9VJFCEcuRlrViGj8pJUFFvNS4EkEREaN7CB1EcLXJIaVSSa5eCihwBGXtOZxhnblWgxks9juRdNQI7w==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^3.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.58.0.tgz", + "integrity": "sha512-hr6FNvmcAXiH+JxSvaJ4SJ1HofkdqEElXICW9sm3/Rd5eC3t7kzvmLyRAB3NngKO2wzXRCAm4Z/mGWfrsS4X8w==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.58.0.tgz", + "integrity": "sha512-R+O368VXgRql1K6Xar+FEo7NEwfo13EibPMoTv3sesYQedRXd6m30Dh/7lZMxnrQVFfeo4EOfYIP4FpcgWQNHg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">= 0.6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.58.0.tgz", + "integrity": "sha512-Q0FZiAY/3c4YRj4z3h9K1PgaByrifrfbBoODSeX7gy97UtB7pySPUQfC2B/GbxWU6k7CzQrRy5gME10PltLAFQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/ssri": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", - "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.58.0.tgz", + "integrity": "sha512-Y8FKBABrSPp9H0QkRLHDHOSUgM/309a3IvOVgPcVxYcX70wxJrk608CuTg7w+C6vEd724X5wJoNkBcGYfH7nNQ==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/tuf": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.1.tgz", - "integrity": "sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw==", + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.58.0.tgz", + "integrity": "sha512-bCn5rbiz5My+Bj7M09sDcnqW0QJyINRVxdZ65x1/Y2tGrMwherwK/lpk+HRQCKvXa8pcaQdF5KY5j54VGZLwNg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0", - "tuf-js": "^4.1.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/verify": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", - "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.1.0", - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" + "@noble/hashes": "^1.1.5" } }, - "node_modules/@simple-libs/child-process-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.0.tgz", - "integrity": "sha512-yUjZwZS8An/un6iyC0/HJMbWyEvp8y3RSW5DnxgfVXdDs9OLdulWMPs2EQLZkulsxCm3JohkB3JbyVsfeondkg==", + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", - "dependencies": { - "@simple-libs/stream-utils": "^1.0.0", - "@types/node": "^22.0.0" - }, + "optional": true, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" + "node": ">=14" } }, - "node_modules/@simple-libs/child-process-utils/node_modules/@types/node": { - "version": "22.15.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz", - "integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==", + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } + "license": "BSD-3-Clause" }, - "node_modules/@simple-libs/child-process-utils/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause" }, - "node_modules/@simple-libs/stream-utils": { + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.1.0.tgz", - "integrity": "sha512-6rsHTjodIn/t90lv5snQjRPVtOosM7Vp0AKdrObymq45ojlgVwnpAqdc+0OBBrpEiy31zZ6/TKeIVqV1HwvnuQ==", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^22.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" - } + "license": "BSD-3-Clause" }, - "node_modules/@simple-libs/stream-utils/node_modules/@types/node": { - "version": "22.15.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz", - "integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==", + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "undici-types": "~6.21.0" + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" } }, - "node_modules/@simple-libs/stream-utils/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause" }, - "node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause" }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } + "license": "BSD-3-Clause" }, - "node_modules/@sinonjs/commons/node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@redis/bloom": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.10.0.tgz", + "integrity": "sha512-doIF37ob+l47n0rkpRNgU8n4iacBlKM9xLiP1LtTZTvz8TloJB8qx/MgvhMhKdYG+CvCY2aPBnN2706izFn/4A==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.10.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.0.tgz", - "integrity": "sha512-cqfapCxwTGsrR80FEgOoPsTonoefMBY7dnUEbQ+GRcved0jvkJLzvX6F4WtN+HBqbPX/SiFsIRUp+IrCW/2I2w==", + "node_modules/@redis/client": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.10.0.tgz", + "integrity": "sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.1" + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" } }, - "node_modules/@sinonjs/samsam": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", - "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "node_modules/@redis/json": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.10.0.tgz", + "integrity": "sha512-B2G8XlOmTPUuZtD44EMGbtoepQG34RCDXLZbjrtON1Djet0t5Ri7/YPXvL9aomXqP8lLTreaprtyLKF4tmXEEA==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "type-detect": "^4.1.0" + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.10.0" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" - }, - "node_modules/@sqltools/formatter": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", - "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", - "dev": true - }, - "node_modules/@tokenizer/inflate": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", - "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "node_modules/@redis/search": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.10.0.tgz", + "integrity": "sha512-3SVcPswoSfp2HnmWbAGUzlbUPn7fOohVu2weUQ0S+EMiQi8jwjL+aN2p6V3TI65eNfVsJ8vyPvqWklm6H6esmg==", + "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "token-types": "^6.1.1" - }, "engines": { - "node": ">=18" + "node": ">= 18" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "peerDependencies": { + "@redis/client": "^5.10.0" } }, - "node_modules/@tokenizer/inflate/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@redis/time-series": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.10.0.tgz", + "integrity": "sha512-cPkpddXH5kc/SdRhF0YG0qtjL+noqFT0AcHbQ6axhsPsO7iqPi1cjxgdkE9TNeKiBUUdCaU1DbqkR/LzbzPBhg==", + "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" + "node": ">= 18" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@redis/client": "^5.10.0" } }, - "node_modules/@tokenizer/inflate/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT" - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@ts-morph/common": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz", - "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "minimatch": "^10.0.1", - "path-browserify": "^1.0.1", - "tinyglobby": "^0.2.14" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", - "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@tufjs/models": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", - "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^10.1.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/amqplib": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.8.tgz", - "integrity": "sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/chai": { - "version": "4.3.20", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", - "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@types/chai-as-promised": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", - "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/chai": "*" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@types/node": "*" + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@types/eslint__js": { - "version": "8.42.3", - "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", - "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@types/eslint": "*" + "tslib": "^2.4.0" } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/expect": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", - "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", - "dev": true - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/express/node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/glob-stream": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-8.0.2.tgz", - "integrity": "sha512-kyuRfGE+yiSJWzSO3t74rXxdZNdYfLcllO0IUha4eX1fl40pm9L02Q/TEc3mykTLjoWz4STBNwYnUWdFu3I0DA==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", "dev": true, - "dependencies": { - "@types/node": "*", - "@types/picomatch": "*", - "@types/streamx": "*" - } + "license": "MIT" }, - "node_modules/@types/gulp": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.18.tgz", - "integrity": "sha512-IqkYa4sXkwH2uwqO2aXYOoAisJpLX13BPaS6lmEAoG4BbgOay3qqGQFsT9LMSSQVMQlEKU7wTUW0sPV46V0olw==", + "node_modules/@sigstore/bundle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*", - "@types/undertaker": ">=1.2.6", - "@types/vinyl-fs": "*", - "chokidar": "^3.3.1" + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/gulp/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/@sigstore/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.0.tgz", + "integrity": "sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==", "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" - }, + "license": "Apache-2.0", "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/gulp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/@sigstore/protobuf-specs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz", + "integrity": "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@types/gulp/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/@sigstore/sign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz", + "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "picomatch": "^2.2.1" + "@gar/promise-retry": "^1.0.2", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.4", + "proc-log": "^6.1.0" }, "engines": { - "node": ">=8.10.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "dev": true - }, - "node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "node_modules/@sigstore/sign/node_modules/@npmcli/redact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, - "node_modules/@types/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.5.tgz", + "integrity": "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/mocha": { - "version": "10.0.10", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", - "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", - "dev": true - }, - "node_modules/@types/node": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", - "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", - "license": "MIT", + "license": "ISC", "dependencies": { - "undici-types": "~7.16.0" + "@gar/promise-retry": "^1.0.0", + "@npmcli/agent": "^4.0.0", + "@npmcli/redact": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-1MRgzpzY0hOp9pW/kLRxeQhUWwil6gnrUYd3oEpeYBqp/FexhaCPv3F8LsYr47gtUU45fO2cm1dbwkSrHEo8Uw==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.17", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", - "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/readable-stream": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.21.tgz", - "integrity": "sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==", + "node_modules/@sigstore/sign/node_modules/minipass-fetch": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", + "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "minipass": "^7.0.3", + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + }, + "optionalDependencies": { + "iconv-lite": "^0.7.2" } }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "node_modules/@sigstore/sign/node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", "dev": true, + "license": "ISC", "dependencies": { - "@types/mime": "^1", - "@types/node": "*" + "minipass": "^7.1.2" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/sinon": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.0.tgz", - "integrity": "sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==", + "node_modules/@sigstore/sign/node_modules/ssri": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@types/sinonjs__fake-timers": "*" + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", - "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", - "dev": true - }, - "node_modules/@types/streamx": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/@types/streamx/-/streamx-2.9.5.tgz", - "integrity": "sha512-IHYsa6jYrck8VEdSwpY141FTTf6D7boPeMq9jy4qazNrFMA4VbRz/sw5LSsfR7jwdDcx0QKWkUexZvsWBC2eIQ==", + "node_modules/@sigstore/tuf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz", + "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/superagent": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", + "node_modules/@sigstore/verify": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", + "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/supertest": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", - "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", + "node_modules/@simple-libs/child-process-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", + "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", "dev": true, "license": "MIT", "dependencies": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" + "@simple-libs/stream-utils": "^1.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" } }, - "node_modules/@types/undertaker": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.11.tgz", - "integrity": "sha512-j1Z0V2ByRHr8ZK7eOeGq0LGkkdthNFW0uAZGY22iRkNQNL9/vAV0yFPr1QN3FM/peY5bxs9P+1f0PYJTQVa5iA==", + "node_modules/@simple-libs/stream-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", + "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", "dev": true, - "dependencies": { - "@types/node": "*", - "@types/undertaker-registry": "*", - "async-done": "~1.3.2" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" } }, - "node_modules/@types/undertaker-registry": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.4.tgz", - "integrity": "sha512-tW77pHh2TU4uebWXWeEM5laiw8BuJ7pyJYDh6xenOs75nhny2kVgwYbegJ4BoLMYsIrXaBpKYaPdYO3/udG+hg==", - "dev": true + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, + "license": "MIT" }, - "node_modules/@types/validator": { - "version": "13.15.10", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", - "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "license": "MIT" }, - "node_modules/@types/vinyl": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", - "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", "dev": true, - "dependencies": { - "@types/expect": "^1.20.4", - "@types/node": "*" - } + "license": "MIT" }, - "node_modules/@types/vinyl-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-3.0.5.tgz", - "integrity": "sha512-ckYz9giHgV6U10RFuf9WsDQ3X86EFougapxHmmoxLK7e6ICQqO8CE+4V/3lBN148V5N1pb4nQMmMjyScleVsig==", + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", "dependencies": { - "@types/glob-stream": "*", - "@types/node": "*", - "@types/vinyl": "*" + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", - "dev": true, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" + "engines": { + "node": ">= 6" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", - "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", + "node_modules/@ts-morph/common": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz", + "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/type-utils": "8.55.0", - "@typescript-eslint/utils": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.55.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "minimatch": "^10.0.1", + "path-browserify": "^1.0.1", + "tinyglobby": "^0.2.14" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/@ts-morph/common/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", - "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", - "debug": "^4.4.3" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "ms": "^2.1.3" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=6.0" + "node": "18 || 20 || >=22" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/parser/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", "dev": true, "license": "MIT" }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", - "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.55.0", - "@typescript-eslint/types": "^8.55.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } + "license": "MIT" }, - "node_modules/@typescript-eslint/project-service/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } + "license": "MIT" }, - "node_modules/@typescript-eslint/project-service/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true, "license": "MIT" }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", - "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", - "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", + "node_modules/@tufjs/models": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", "dev": true, "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^10.1.1" }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", - "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "node_modules/@tufjs/models/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/utils": "8.55.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/types": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", - "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "18 || 20 || >=22" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", - "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", - "dev": true, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.55.0", - "@typescript-eslint/tsconfig-utils": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/@types/amqplib": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.8.tgz", + "integrity": "sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@types/node": "*" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", - "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@types/node": "*" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", - "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" } }, - "node_modules/@whatwg-node/promise-helpers": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", - "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.6.3" - }, - "engines": { - "node": ">=16.0.0" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/@yarnpkg/parsers": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.2.tgz", - "integrity": "sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==", + "node_modules/@types/glob-stream": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-8.0.3.tgz", + "integrity": "sha512-vctgrT9AH/GK3TRaIbRUU0TZn12GBU4kzelZdPyJp1Sc8L/6Wrq21UrtN4+x4saqTg6COUIUtFV6JSYcVln/EQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "js-yaml": "^3.10.0", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=18.12.0" + "@types/node": "*", + "@types/picomatch": "*", + "@types/streamx": "*" } }, - "node_modules/@zkochan/js-yaml": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz", - "integrity": "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==", + "node_modules/@types/gulp": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.18.tgz", + "integrity": "sha512-IqkYa4sXkwH2uwqO2aXYOoAisJpLX13BPaS6lmEAoG4BbgOay3qqGQFsT9LMSSQVMQlEKU7wTUW0sPV46V0olw==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "@types/node": "*", + "@types/undertaker": ">=1.2.6", + "@types/vinyl-fs": "*", + "chokidar": "^3.3.1" } }, - "node_modules/@zkochan/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "node_modules/@types/gulp/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">= 8" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "node_modules/@types/gulp/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "license": "MIT", "dependencies": { - "event-target-shim": "^5.0.0" + "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": ">=6.5" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@types/gulp/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, + "license": "ISC", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "is-glob": "^4.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 6" } }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "node_modules/@types/gulp/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "picomatch": "^2.2.1" }, "engines": { - "node": ">=0.4.0" + "node": ">=8.10.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } + "license": "MIT" }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true, + "license": "MIT" }, - "node_modules/add-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", - "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", "dev": true, "license": "MIT" }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } + "license": "MIT" }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz", + "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==", + "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "undici-types": "~7.16.0" } }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "node_modules/@types/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/readable-stream": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz", + "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", "dev": true, "license": "MIT", "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" + "@types/node": "*" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, + "license": "MIT", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@types/node": "*" } }, - "node_modules/aggregate-error/node_modules/clean-stack": { + "node_modules/@types/serve-static": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, - "engines": { - "node": ">=6" + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" } }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/@types/streamx": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@types/streamx/-/streamx-2.9.5.tgz", + "integrity": "sha512-IHYsa6jYrck8VEdSwpY141FTTf6D7boPeMq9jy4qazNrFMA4VbRz/sw5LSsfR7jwdDcx0QKWkUexZvsWBC2eIQ==", + "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "@types/node": "*" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "node_modules/@types/superagent": { + "version": "8.1.9", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", + "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", + "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" } }, - "node_modules/amqp-connection-manager": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/amqp-connection-manager/-/amqp-connection-manager-5.0.0.tgz", - "integrity": "sha512-88yQzqa5RSBgnLl504XjvCQJ7d+osskdwvg35Lwm1LRbfLjNU9p7SQUMSP82BB7mseiq9tIUPJ3HE3eXQbpjEw==", + "node_modules/@types/supertest": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", + "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", "dev": true, "license": "MIT", "dependencies": { - "promise-breaker": "^6.0.0" - }, - "engines": { - "node": ">=10.0.0", - "npm": ">5.0.0" - }, - "peerDependencies": { - "amqplib": "*" + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" } }, - "node_modules/amqplib": { - "version": "0.10.9", - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.9.tgz", - "integrity": "sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==", + "node_modules/@types/undertaker": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.12.tgz", + "integrity": "sha512-52BiBni1srlIx/o7anEB1Y230yr3+21P0utA4VXLyeyeR2gHANKi5kJ/e0FakD4RYEXX0D9dOC7PDrVqL1j98Q==", "dev": true, "license": "MIT", "dependencies": { - "buffer-more-ints": "~1.0.0", - "url-parse": "~1.5.10" - }, - "engines": { - "node": ">=10" + "@types/node": "*", + "@types/undertaker-registry": "*", + "async-done": "~1.3.2" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/@types/undertaker-registry": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.4.tgz", + "integrity": "sha512-tW77pHh2TU4uebWXWeEM5laiw8BuJ7pyJYDh6xenOs75nhny2kVgwYbegJ4BoLMYsIrXaBpKYaPdYO3/udG+hg==", "dev": true, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/@types/vinyl": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", + "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "@types/expect": "^1.20.4", + "@types/node": "*" } }, - "node_modules/ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "node_modules/@types/vinyl-fs": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-3.0.7.tgz", + "integrity": "sha512-ojGFhBnh5pj5Crf2yBOk3rjJXUX2U4W9z6tZ7hn6pUbQa/J8KH8NrXem0POYVQWI3ifnx4T65DPktuWfxc3iiA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "@types/glob-stream": "*", + "@types/node": "*", + "@types/vinyl": "*" } }, - "node_modules/ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "@types/webidl-conversions": "*" } }, - "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/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "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==", + "node_modules/@vitest/coverage-istanbul": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-4.1.2.tgz", + "integrity": "sha512-WSz7+4a7PcMtMNvIP7AXUMffsq4JrWeJaguC8lg6fSQyGxSfaT4Rf81idqwxTT6qX5kjjZw2t9rAnCRRQobSqw==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@babel/core": "^7.29.0", + "@istanbuljs/schema": "^0.1.3", + "@jridgewell/gen-mapping": "^0.3.13", + "@jridgewell/trace-mapping": "0.3.31", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "tinyrainbow": "^3.1.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.2" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.2.tgz", + "integrity": "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.2", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.2", + "vitest": "4.1.2" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, - "node_modules/ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "node_modules/@vitest/expect": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", + "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/ansis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", - "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", - "license": "ISC", - "engines": { - "node": ">=14" + "node_modules/@vitest/mocker": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", + "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.2", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "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/@vitest/pretty-format": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", + "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "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==", + "node_modules/@vitest/runner": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", + "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@vitest/utils": "4.1.2", + "pathe": "^2.0.3" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/@vitest/snapshot": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", + "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8.6" + "dependencies": { + "@vitest/pretty-format": "4.1.2", + "@vitest/utils": "4.1.2", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://opencollective.com/vitest" } }, - "node_modules/app-root-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", - "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "node_modules/@vitest/spy": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", + "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", "dev": true, - "engines": { - "node": ">= 6.0.0" + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "node_modules/@vitest/utils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", + "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-equal": "^1.0.0" + "@vitest/pretty-format": "4.1.2", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "dev": true - }, - "node_modules/append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "node_modules/@whatwg-node/promise-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", "dev": true, + "license": "MIT", "dependencies": { - "default-require-extensions": "^2.0.0" + "tslib": "^2.6.3" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true, - "license": "ISC" - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true + "license": "BSD-2-Clause" }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "node_modules/@yarnpkg/parsers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.2.tgz", + "integrity": "sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=18.12.0" + } }, - "node_modules/argparse": { + "node_modules/@yarnpkg/parsers/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "node_modules/@zkochan/js-yaml": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz", + "integrity": "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", "dev": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.5" } }, - "node_modules/array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", - "dev": true, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true - }, - "node_modules/array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "dev": true, "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { - "node": ">=8" + "node": ">=0.4.0" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 14" } }, - "node_modules/array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 8.0.0" } }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", "dependencies": { - "safer-buffer": "~2.1.0" + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "node_modules/amqp-connection-manager": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/amqp-connection-manager/-/amqp-connection-manager-5.0.0.tgz", + "integrity": "sha512-88yQzqa5RSBgnLl504XjvCQJ7d+osskdwvg35Lwm1LRbfLjNU9p7SQUMSP82BB7mseiq9tIUPJ3HE3eXQbpjEw==", "dev": true, + "license": "MIT", + "dependencies": { + "promise-breaker": "^6.0.0" + }, "engines": { - "node": ">=0.8" + "node": ">=10.0.0", + "npm": ">5.0.0" + }, + "peerDependencies": { + "amqplib": "*" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "node_modules/amqplib": { + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.9.tgz", + "integrity": "sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==", "dev": true, "license": "MIT", + "dependencies": { + "buffer-more-ints": "~1.0.0", + "url-parse": "~1.5.10" + }, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/async-done": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "node_modules/ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", "dev": true, + "license": "MIT", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^2.0.0", - "stream-exhaust": "^1.0.1" + "ansi-wrap": "0.1.0" }, "engines": { - "node": ">= 0.10" - } - }, - "node_modules/async-each": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", - "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dev": true, - "dependencies": { - "retry": "0.13.1" + "node": ">=0.10.0" } }, - "node_modules/async-settle": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-2.0.0.tgz", - "integrity": "sha512-Obu/KE8FurfQRN6ODdHN9LuXqwC+JFIM9NRyZqJJ4ZfLJmIYN9Rg0/kb+wF70VV5+fJusTMQlJ1t5rF7J/ETdg==", + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", "dev": true, + "license": "MIT", "dependencies": { - "async-done": "^2.0.0" + "environment": "^1.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/async-settle/node_modules/async-done": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-2.0.0.tgz", - "integrity": "sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw==", + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", "dev": true, + "license": "MIT", "dependencies": { - "end-of-stream": "^1.4.4", - "once": "^1.4.0", - "stream-exhaust": "^1.0.2" + "ansi-wrap": "0.1.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">=0.10.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", "dev": true, - "bin": { - "atob": "bin/atob.js" + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" }, "engines": { - "node": ">= 4.5.0" + "node": ">=0.10.0" } }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": ">=0.10.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "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, + "license": "MIT", "dependencies": { - "possible-typed-array-names": "^1.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/avvio": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", - "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", "dev": true, - "dependencies": { - "@fastify/error": "^4.0.0", - "fastq": "^1.17.1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "license": "ISC", "engines": { - "node": "*" + "node": ">=14" } }, - "node_modules/aws-ssl-profiles": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", - "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "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, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aws4": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "dev": true + "license": "MIT" }, - "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "node_modules/anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" } }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/bach": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bach/-/bach-2.0.1.tgz", - "integrity": "sha512-A7bvGMGiTOxGMpNupYl9HQTf0FFDNF4VCmks4PJpFyN1AX2pdKuxuwdvUz2Hu388wcgp+OvGFNsumBfFNkR7eg==", + "node_modules/anymatch/node_modules/arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", "dev": true, + "license": "MIT", "dependencies": { - "async-done": "^2.0.0", - "async-settle": "^2.0.0", - "now-and-later": "^3.0.0" + "arr-flatten": "^1.0.1" }, "engines": { - "node": ">=10.13.0" + "node": ">=0.10.0" } }, - "node_modules/bach/node_modules/async-done": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-2.0.0.tgz", - "integrity": "sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw==", + "node_modules/anymatch/node_modules/braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", "dev": true, + "license": "MIT", "dependencies": { - "end-of-stream": "^1.4.4", - "once": "^1.4.0", - "stream-exhaust": "^1.0.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" }, "engines": { - "node": ">= 10.13.0" + "node": ">=0.10.0" } }, - "node_modules/backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "node_modules/anymatch/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "dev": true, - "license": "Apache-2.0", - "optional": true + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/anymatch/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", "dev": true, + "license": "MIT", "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "is-extglob": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "node_modules/anymatch/node_modules/micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.0" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, + "license": "MIT", "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" + "remove-trailing-separator": "^1.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", "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/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", "engines": { - "node": "^4.5.0 || >= 5.9" + "node": ">= 6.0.0" } }, - "node_modules/bcrypt-pbkdf": { + "node_modules/append-buffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", "dev": true, + "license": "MIT", "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/beeper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha512-3vqtKL1N45I5dV0RdssXZG7X6pCqQrWPNOlBPZPrd+QkE2HEhR57Z04m0KtpbsZH73j+a3F8UD1TQnn+ExTvIA==", - "dev": true, + "buffer-equal": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "dev": true, - "license": "Apache-2.0" + "license": "ISC" }, - "node_modules/bin-links": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-5.0.0.tgz", - "integrity": "sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==", + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^7.0.0", - "npm-normalize-package-bin": "^4.0.0", - "proc-log": "^5.0.0", - "read-cmd-shim": "^5.0.0", - "write-file-atomic": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } + "license": "MIT" }, - "node_modules/bin-links/node_modules/cmd-shim": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-7.0.0.tgz", - "integrity": "sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } + "license": "Python-2.0" }, - "node_modules/bin-links/node_modules/read-cmd-shim": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz", - "integrity": "sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==", + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=0.10.0" } }, - "node_modules/bin-links/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, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/bin-links/node_modules/write-file-atomic": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", - "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, + "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=0.10.0" } }, - "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==", + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" + "node": ">=0.10.0" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "node_modules/array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", "dev": true, "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true, "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/bl/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } + "license": "MIT" }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true, "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=0.10.0" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "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, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/body-parser/node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/body-parser/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/body-parser/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=8" } }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, "license": "MIT" }, - "node_modules/body-parser/node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, "license": "MIT", "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" + "safer-buffer": "~2.1.0" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "license": "MIT", + "engines": { + "node": ">=0.8" } }, - "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==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/broker-factory": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.8.tgz", - "integrity": "sha512-xmVnYN0FZtynhPUmAnN+/MFRdbDi3syCuxWV7o7s78FcIN0pjDtn9mUrVqEgdjQkbfojRhlPWbYbXJkMCyddrg==", + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.6", - "fast-unique-numbers": "^9.0.22", - "tslib": "^2.8.1", - "worker-factory": "^7.0.44" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", "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" - } - ], "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" } }, - "node_modules/buffer-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", - "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "license": "MIT" }, - "node_modules/buffer-more-ints": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", - "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==", + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "node_modules/async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", "dev": true, + "license": "MIT", "dependencies": { - "streamsearch": "^1.1.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" }, "engines": { - "node": ">=10.16.0" + "node": ">= 0.10" } }, - "node_modules/byte-size": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz", - "integrity": "sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==", + "node_modules/async-each": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", + "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" }, - "node_modules/cacache": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz", - "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==", + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/fs": "^5.0.0", - "fs-minipass": "^3.0.0", - "glob": "^13.0.0", - "lru-cache": "^11.1.0", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^13.0.0", - "unique-filename": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" + "retry": "0.13.1" } }, - "node_modules/cacache/node_modules/@npmcli/fs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", - "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", + "node_modules/async-settle": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-2.0.0.tgz", + "integrity": "sha512-Obu/KE8FurfQRN6ODdHN9LuXqwC+JFIM9NRyZqJJ4ZfLJmIYN9Rg0/kb+wF70VV5+fJusTMQlJ1t5rF7J/ETdg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "semver": "^7.3.5" + "async-done": "^2.0.0" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">= 10.13.0" } }, - "node_modules/cacache/node_modules/glob": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", - "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "node_modules/async-settle/node_modules/async-done": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-2.0.0.tgz", + "integrity": "sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" + "end-of-stream": "^1.4.4", + "once": "^1.4.0", + "stream-exhaust": "^1.0.2" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 10.13.0" } }, - "node_modules/cacache/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "license": "MIT" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 4.5.0" } }, - "node_modules/cacache/node_modules/p-map": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", - "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", - "dev": true, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.0.0" } }, - "node_modules/cacache/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": "20 || >=22" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cacache/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "node_modules/avvio": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz", + "integrity": "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" } }, - "node_modules/cacache/node_modules/ssri": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", - "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, + "license": "Apache-2.0", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "*" } }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 6.0.0" } }, - "node_modules/cache-manager": { - "version": "7.2.8", - "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-7.2.8.tgz", - "integrity": "sha512-0HDaDLBBY/maa/LmUVAr70XUOwsiQD+jyzCBjmUErYZUKdMS9dT59PqW59PpVqfGM7ve6H0J6307JTpkCYefHQ==", + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "dev": true, - "license": "MIT", - "dependencies": { - "@cacheable/utils": "^2.3.3", - "keyv": "^5.5.5" - } + "license": "MIT" }, - "node_modules/cache-manager/node_modules/keyv": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.5.tgz", - "integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==", + "node_modules/axios": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", + "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", "dev": true, "license": "MIT", "dependencies": { - "@keyv/serialize": "^1.1.1" + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" } }, - "node_modules/caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", "dev": true, - "dependencies": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" }, - "engines": { - "node": ">=6" + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } } }, - "node_modules/caching-transform/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/bach": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bach/-/bach-2.0.1.tgz", + "integrity": "sha512-A7bvGMGiTOxGMpNupYl9HQTf0FFDNF4VCmks4PJpFyN1AX2pdKuxuwdvUz2Hu388wcgp+OvGFNsumBfFNkR7eg==", "dev": true, + "license": "MIT", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "async-done": "^2.0.0", + "async-settle": "^2.0.0", + "now-and-later": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=10.13.0" } }, - "node_modules/caching-transform/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/bach/node_modules/async-done": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-2.0.0.tgz", + "integrity": "sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw==", "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.4", + "once": "^1.4.0", + "stream-exhaust": "^1.0.2" + }, "engines": { - "node": ">=6" + "node": ">= 10.13.0" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "node_modules/backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "is-descriptor": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, - "engines": { - "node": ">=6" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "license": "MIT", "engines": { - "node": ">=6" + "node": "^4.5.0 || >= 5.9" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "node_modules/baseline-browser-mapping": { + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.13.tgz", + "integrity": "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==", "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.0.0" } }, - "node_modules/camelcase-keys/node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true + "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" }, - "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "node_modules/beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha512-3vqtKL1N45I5dV0RdssXZG7X6pCqQrWPNOlBPZPrd+QkE2HEhR57Z04m0KtpbsZH73j+a3F8UD1TQnn+ExTvIA==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true, - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } + "license": "Apache-2.0" }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/bin-links": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-5.0.0.tgz", + "integrity": "sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==", "dev": true, + "license": "ISC", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "node_modules/bin-links/node_modules/cmd-shim": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-7.0.0.tgz", + "integrity": "sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "node_modules/bin-links/node_modules/read-cmd-shim": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz", + "integrity": "sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, + "license": "ISC", "engines": { - "node": "*" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/bin-links/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, - "dependencies": { - "readdirp": "^4.0.1" - }, + "license": "ISC", "engines": { - "node": ">= 14.16.0" + "node": ">=14" }, "funding": { - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "node_modules/bin-links/node_modules/write-file-atomic": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, "engines": { - "node": ">=10" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/clang-format": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.8.0.tgz", - "integrity": "sha512-pK8gzfu55/lHzIpQ1givIbWfn3eXnU7SfxqIwVgnn5jEM6j4ZJYjpFqFs4iSBPNedzRMmfjYjuQhu657WAXHXw==", + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "async": "^3.2.3", - "glob": "^7.0.0", - "resolve": "^1.1.6" - }, - "bin": { - "check-clang-format": "bin/check-clang-format.js", - "clang-format": "index.js", - "git-clang-format": "bin/git-clang-format" + "file-uri-to-path": "1.0.0" } }, - "node_modules/clang-format/node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true - }, - "node_modules/class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", "dev": true, + "license": "MIT", "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" } }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/bl/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^0.1.0" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-validator": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", - "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", - "license": "MIT", - "dependencies": { - "@types/validator": "^13.15.3", - "libphonenumber-js": "^1.11.1", - "validator": "^13.15.20" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/cli-color": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", - "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", + "node_modules/bl/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "dependencies": { - "ansi-regex": "^2.1.1", - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.14", - "timers-ext": "^0.1.5" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/cli-color/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { - "restore-cursor": "^5.0.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/cli-highlight": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", - "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "highlight.js": "^10.7.1", - "mz": "^2.4.0", - "parse5": "^5.1.1", - "parse5-htmlparser2-tree-adapter": "^6.0.0", - "yargs": "^16.0.0" - }, - "bin": { - "highlight": "bin/highlight" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/cli-highlight/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/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, + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/cli-highlight/node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true - }, - "node_modules/cli-highlight/node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "node_modules/broker-factory": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.14.tgz", + "integrity": "sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==", "dev": true, + "license": "MIT", "dependencies": { - "parse5": "^6.0.1" + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" } }, - "node_modules/cli-highlight/node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/cli-highlight/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" }, "engines": { - "node": ">=10" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/cli-highlight/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/bson": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz", + "integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": ">=20.19.0" } }, - "node_modules/cli-truncate": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.0.tgz", - "integrity": "sha512-7JDGG+4Zp0CsknDCedl0DYdaeOhc46QNpXi3NLQblkZpXXgA6LncLDUUyvrjSvZeF3VRQa+KiMGomazQrC1V8g==", + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "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" + } + ], "license": "MIT", "dependencies": { - "slice-ansi": "^7.1.0", - "string-width": "^8.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "streamsearch": "^1.1.0" }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.16.0" } }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "node_modules/byte-size": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz", + "integrity": "sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=12.17" } }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { - "node": ">= 12" + "node": ">= 0.8" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/cacache": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz", + "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==", "dev": true, + "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@npmcli/fs": "^5.0.0", + "fs-minipass": "^3.0.0", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^13.0.0" }, "engines": { - "node": ">=12" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "node_modules/cacache/node_modules/@npmcli/fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, "engines": { - "node": ">=0.8" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "node_modules/cacache/node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" - } - }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", - "dev": true - }, - "node_modules/cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cloneable-readable/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/cloneable-readable/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/cacache/node_modules/ssri": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", "dev": true, + "license": "ISC", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/cloneable-readable/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/cloneable-readable/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "dev": true, + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/cmd-shim": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.3.tgz", - "integrity": "sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==", + "node_modules/cache-manager": { + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-7.2.8.tgz", + "integrity": "sha512-0HDaDLBBY/maa/LmUVAr70XUOwsiQD+jyzCBjmUErYZUKdMS9dT59PqW59PpVqfGM7ve6H0J6307JTpkCYefHQ==", "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.3.3", + "keyv": "^5.5.5" } }, - "node_modules/code-block-writer": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", - "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", - "dev": true - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, + "license": "MIT", "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "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/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" + "node": ">= 0.4" } }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/columnify": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", - "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", - "dev": true, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/commist": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", - "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", - "dev": true - }, - "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "dev": true, - "license": "ISC" - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "license": "MIT", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" + "engines": { + "node": ">=6" } }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "node_modules/caniuse-lite": { + "version": "1.0.30001784", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", + "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", "dev": true, - "engines": [ - "node >= 6.0" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } + "license": "CC-BY-4.0" }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } + "license": "Apache-2.0" }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "engines": { + "node": ">=18" } }, - "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "4.1.2", - "rxjs": "7.8.2", - "shell-quote": "1.8.3", - "supports-color": "8.1.1", - "tree-kill": "1.2.2", - "yargs": "17.7.2" - }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=18" + "node": ">=10" }, "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, "engines": { - "node": ">=18" + "node": ">= 14.16.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/conventional-changelog": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-7.1.1.tgz", - "integrity": "sha512-rlqa8Lgh8YzT3Akruk05DR79j5gN9NCglHtJZwpi6vxVeaoagz+84UAtKQj/sT+RsfGaZkt3cdFCjcN6yjr5sw==", + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clang-format": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.8.0.tgz", + "integrity": "sha512-pK8gzfu55/lHzIpQ1givIbWfn3eXnU7SfxqIwVgnn5jEM6j4ZJYjpFqFs4iSBPNedzRMmfjYjuQhu657WAXHXw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@conventional-changelog/git-client": "^2.5.1", - "@types/normalize-package-data": "^2.4.4", - "conventional-changelog-preset-loader": "^5.0.0", - "conventional-changelog-writer": "^8.2.0", - "conventional-commits-parser": "^6.2.0", - "fd-package-json": "^2.0.0", - "meow": "^13.0.0", - "normalize-package-data": "^7.0.0" + "async": "^3.2.3", + "glob": "^7.0.0", + "resolve": "^1.1.6" }, "bin": { - "conventional-changelog": "dist/cli/index.js" - }, - "engines": { - "node": ">=18" + "check-clang-format": "bin/check-clang-format.js", + "clang-format": "index.js", + "git-clang-format": "bin/git-clang-format" } }, - "node_modules/conventional-changelog-angular": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", - "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "node_modules/clang-format/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { - "compare-func": "^2.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=16" - } - }, - "node_modules/conventional-changelog-core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-5.0.1.tgz", - "integrity": "sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "add-stream": "^1.0.0", - "conventional-changelog-writer": "^6.0.0", - "conventional-commits-parser": "^4.0.0", - "dateformat": "^3.0.3", - "get-pkg-repo": "^4.2.1", - "git-raw-commits": "^3.0.0", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^5.0.0", - "normalize-package-data": "^3.0.3", - "read-pkg": "^3.0.0", - "read-pkg-up": "^3.0.0" + "node": "*" }, - "engines": { - "node": ">=14" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/conventional-changelog-core/node_modules/conventional-changelog-writer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-6.0.1.tgz", - "integrity": "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-commits-filter": "^3.0.0", - "dateformat": "^3.0.3", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "meow": "^8.1.2", - "semver": "^7.0.0", - "split": "^1.0.1" - }, - "bin": { - "conventional-changelog-writer": "cli.js" - }, - "engines": { - "node": ">=14" - } + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" }, - "node_modules/conventional-changelog-core/node_modules/conventional-commits-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", - "integrity": "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==", + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "license": "MIT", "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.1" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "engines": { - "node": ">=14" + "node": ">=0.10.0" } }, - "node_modules/conventional-changelog-core/node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, + "node_modules/class-validator": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", + "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", "license": "MIT", "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=14" + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.20" } }, - "node_modules/conventional-changelog-core/node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/conventional-changelog-core/node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "node_modules/cli-color": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", + "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", "dev": true, - "license": "MIT", - "engines": { - "node": "*" + "license": "ISC", + "dependencies": { + "ansi-regex": "^2.1.1", + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.14", + "timers-ext": "^0.1.5" } }, - "node_modules/conventional-changelog-core/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-core/node_modules/git-raw-commits": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-3.0.0.tgz", - "integrity": "sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==", + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "dargs": "^7.0.0", - "meow": "^8.1.2", - "split2": "^3.2.2" + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" }, "bin": { - "git-raw-commits": "cli.js" + "highlight": "bin/highlight" }, "engines": { - "node": ">=14" + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "license": "ISC", "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/conventional-changelog-core/node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "node_modules/cli-highlight/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { - "text-extensions": "^1.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/conventional-changelog-core/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/conventional-changelog-core/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { "node": ">=10" } }, - "node_modules/conventional-changelog-core/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true, "license": "MIT", - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-core/node_modules/meow/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, - "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg": { + "node_modules/cli-truncate": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", "dev": true, "license": "MIT", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/cli-truncate/node_modules/string-width": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", + "ansi-regex": "^6.2.2" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/conventional-changelog-core/node_modules/meow/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">= 12" } }, - "node_modules/conventional-changelog-core/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "ISC", "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/conventional-changelog-core/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/conventional-changelog-core/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, "engines": { - "node": ">=8" + "node": ">=0.8" } }, - "node_modules/conventional-changelog-core/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/conventional-changelog-core/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" } }, - "node_modules/conventional-changelog-core/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/conventional-changelog-core/node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "node_modules/cmd-shim": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.3.tgz", + "integrity": "sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==", "dev": true, "license": "ISC", - "dependencies": { - "readable-stream": "^3.0.0" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/conventional-changelog-core/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/conventional-changelog-core/node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "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, "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=0.10" + "node": ">=7.0.0" } }, - "node_modules/conventional-changelog-core/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "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, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/conventional-changelog-core/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true, "license": "ISC", - "engines": { - "node": ">=10" + "bin": { + "color-support": "bin.js" } }, - "node_modules/conventional-changelog-preset-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-5.0.0.tgz", - "integrity": "sha512-SetDSntXLk8Jh1NOAl1Gu5uLiCNSYenB5tm0YVeZKePRIgDW9lQImromTwLa3c/Gae298tsgOM+/CYT9XAl0NA==", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/conventional-changelog-writer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.2.0.tgz", - "integrity": "sha512-Y2aW4596l9AEvFJRwFGJGiQjt2sBYTjPD18DdvxX9Vpz0Z7HQ+g1Z+6iYDAm1vR3QOJrDBkRHixHK/+FhkR6Pw==", + "node_modules/columnify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-filter": "^5.0.0", - "handlebars": "^4.7.7", - "meow": "^13.0.0", - "semver": "^7.5.2" - }, - "bin": { - "conventional-changelog-writer": "dist/cli/index.js" + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" }, "engines": { - "node": ">=18" + "node": ">=8.0.0" } }, - "node_modules/conventional-changelog-writer/node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "delayed-stream": "~1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.8" } }, - "node_modules/conventional-changelog-writer/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=20" } }, - "node_modules/conventional-changelog/node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } + "license": "MIT" }, - "node_modules/conventional-changelog/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==", + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", "dev": true, "license": "ISC" }, - "node_modules/conventional-changelog/node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" - }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog/node_modules/normalize-package-data": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-7.0.0.tgz", - "integrity": "sha512-k6U0gKRIuNCTkwHGZqblCfLfBRh+w1vI6tBo+IeJwq2M8FUiOqhX7GH+GArQGScA7azd1WfyRCvxoXDO3hQDIA==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", "dependencies": { - "hosted-git-info": "^8.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">= 6" } }, - "node_modules/conventional-changelog/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/conventional-commits-filter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", - "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true, + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/conventional-commits-parser": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.2.1.tgz", - "integrity": "sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/conventional-changelog": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-7.1.1.tgz", + "integrity": "sha512-rlqa8Lgh8YzT3Akruk05DR79j5gN9NCglHtJZwpi6vxVeaoagz+84UAtKQj/sT+RsfGaZkt3cdFCjcN6yjr5sw==", "dev": true, "license": "MIT", "dependencies": { - "meow": "^13.0.0" + "@conventional-changelog/git-client": "^2.5.1", + "@types/normalize-package-data": "^2.4.4", + "conventional-changelog-preset-loader": "^5.0.0", + "conventional-changelog-writer": "^8.2.0", + "conventional-commits-parser": "^6.2.0", + "fd-package-json": "^2.0.0", + "meow": "^13.0.0", + "normalize-package-data": "^7.0.0" }, "bin": { - "conventional-commits-parser": "dist/cli/index.js" + "conventional-changelog": "dist/cli/index.js" }, "engines": { "node": ">=18" } }, - "node_modules/conventional-commits-parser/node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "node_modules/conventional-changelog-angular": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.1.tgz", + "integrity": "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-recommended-bump": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-7.0.1.tgz", - "integrity": "sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==", + "node_modules/conventional-changelog-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-5.0.1.tgz", + "integrity": "sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==", "dev": true, "license": "MIT", "dependencies": { - "concat-stream": "^2.0.0", - "conventional-changelog-preset-loader": "^3.0.0", - "conventional-commits-filter": "^3.0.0", + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^6.0.0", "conventional-commits-parser": "^4.0.0", + "dateformat": "^3.0.3", + "get-pkg-repo": "^4.2.1", "git-raw-commits": "^3.0.0", + "git-remote-origin-url": "^2.0.0", "git-semver-tags": "^5.0.0", - "meow": "^8.1.2" - }, - "bin": { - "conventional-recommended-bump": "cli.js" + "normalize-package-data": "^3.0.3", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0" }, "engines": { "node": ">=14" } }, - "node_modules/conventional-recommended-bump/node_modules/conventional-changelog-preset-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-3.0.0.tgz", - "integrity": "sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==", + "node_modules/conventional-changelog-core/node_modules/conventional-changelog-writer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-6.0.1.tgz", + "integrity": "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==", "dev": true, "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^3.0.0", + "dateformat": "^3.0.3", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^8.1.2", + "semver": "^7.0.0", + "split": "^1.0.1" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, "engines": { "node": ">=14" } }, - "node_modules/conventional-recommended-bump/node_modules/conventional-commits-filter": { + "node_modules/conventional-changelog-core/node_modules/conventional-commits-filter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", "integrity": "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==", @@ -9018,7 +8322,7 @@ "node": ">=14" } }, - "node_modules/conventional-recommended-bump/node_modules/conventional-commits-parser": { + "node_modules/conventional-changelog-core/node_modules/conventional-commits-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", @@ -9037,17 +8341,17 @@ "node": ">=14" } }, - "node_modules/conventional-recommended-bump/node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "node_modules/conventional-changelog-core/node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/conventional-recommended-bump/node_modules/find-up": { + "node_modules/conventional-changelog-core/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", @@ -9061,10 +8365,11 @@ "node": ">=8" } }, - "node_modules/conventional-recommended-bump/node_modules/git-raw-commits": { + "node_modules/conventional-changelog-core/node_modules/git-raw-commits": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-3.0.0.tgz", "integrity": "sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==", + "deprecated": "This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead.", "dev": true, "license": "MIT", "dependencies": { @@ -9079,7 +8384,7 @@ "node": ">=14" } }, - "node_modules/conventional-recommended-bump/node_modules/hosted-git-info": { + "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", @@ -9092,20 +8397,7 @@ "node": ">=10" } }, - "node_modules/conventional-recommended-bump/node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/conventional-recommended-bump/node_modules/locate-path": { + "node_modules/conventional-changelog-core/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", @@ -9118,7 +8410,7 @@ "node": ">=8" } }, - "node_modules/conventional-recommended-bump/node_modules/lru-cache": { + "node_modules/conventional-changelog-core/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", @@ -9131,7 +8423,7 @@ "node": ">=10" } }, - "node_modules/conventional-recommended-bump/node_modules/meow": { + "node_modules/conventional-changelog-core/node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", @@ -9157,62 +8449,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-recommended-bump/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-recommended-bump/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/conventional-changelog-core/node_modules/meow/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg": { + "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", @@ -9228,7 +8472,7 @@ "node": ">=8" } }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg-up": { + "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg-up": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", @@ -9246,7 +8490,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg-up/node_modules/type-fest": { + "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", @@ -9256,14 +8500,7 @@ "node": ">=8" } }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/normalize-package-data": { + "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", @@ -9276,7 +8513,17 @@ "validate-npm-package-license": "^3.0.1" } }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/semver": { + "node_modules/conventional-changelog-core/node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-changelog-core/node_modules/meow/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", @@ -9286,17 +8533,52 @@ "semver": "bin/semver" } }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/conventional-changelog-core/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { "node": ">=8" } }, - "node_modules/conventional-recommended-bump/node_modules/readable-stream": { + "node_modules/conventional-changelog-core/node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", @@ -9311,20 +8593,7 @@ "node": ">= 6" } }, - "node_modules/conventional-recommended-bump/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump/node_modules/split2": { + "node_modules/conventional-changelog-core/node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", @@ -9334,27 +8603,7 @@ "readable-stream": "^3.0.0" } }, - "node_modules/conventional-recommended-bump/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/conventional-recommended-bump/node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/conventional-recommended-bump/node_modules/type-fest": { + "node_modules/conventional-changelog-core/node_modules/type-fest": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", @@ -9367,7 +8616,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-recommended-bump/node_modules/yargs-parser": { + "node_modules/conventional-changelog-core/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/conventional-changelog-core/node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", @@ -9377,1628 +8633,1707 @@ "node": ">=10" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "node_modules/conventional-changelog-preset-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-5.0.0.tgz", + "integrity": "sha512-SetDSntXLk8Jh1NOAl1Gu5uLiCNSYenB5tm0YVeZKePRIgDW9lQImromTwLa3c/Gae298tsgOM+/CYT9XAl0NA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "node_modules/conventional-changelog-writer": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.4.0.tgz", + "integrity": "sha512-HHBFkk1EECxxmCi4CTu091iuDpQv5/OavuCUAuZmrkWpmYfyD816nom1CvtfXJ/uYfAAjavgHvXHX291tSLK8g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/copy-props": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-4.0.0.tgz", - "integrity": "sha512-bVWtw1wQLzzKiYROtvNlbJgxgBYt2bMJpkCbKmXM3xyijvcjjWXEk5nyrrT3bgJ7ODb19ZohE2T0Y3FgNPyoTw==", + "node_modules/conventional-commits-parser": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", + "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==", "dev": true, "license": "MIT", "dependencies": { - "each-props": "^3.0.0", - "is-plain-object": "^5.0.0" + "@simple-libs/stream-utils": "^1.2.0", + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" }, "engines": { - "node": ">= 10.13.0" + "node": ">=18" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "node_modules/conventional-recommended-bump": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-7.0.1.tgz", + "integrity": "sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==", + "dev": true, "license": "MIT", "dependencies": { - "object-assign": "^4", - "vary": "^1" + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^3.0.0", + "conventional-commits-filter": "^3.0.0", + "conventional-commits-parser": "^4.0.0", + "git-raw-commits": "^3.0.0", + "git-semver-tags": "^5.0.0", + "meow": "^8.1.2" }, - "engines": { - "node": ">= 0.10" + "bin": { + "conventional-recommended-bump": "cli.js" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "engines": { + "node": ">=14" } }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "node_modules/conventional-recommended-bump/node_modules/conventional-changelog-preset-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-3.0.0.tgz", + "integrity": "sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==", "dev": true, "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, "engines": { "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", - "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", + "node_modules/conventional-recommended-bump/node_modules/conventional-commits-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", + "integrity": "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==", "dev": true, "license": "MIT", "dependencies": { - "jiti": "^2.6.1" + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.1" }, "engines": { - "node": ">=v18" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=9", - "typescript": ">=5" + "node": ">=14" } }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/conventional-recommended-bump/node_modules/conventional-commits-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", "dev": true, - "license": "Python-2.0" + "license": "MIT", + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=14" + } }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "node_modules/conventional-recommended-bump/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" } }, - "node_modules/coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", + "node_modules/conventional-recommended-bump/node_modules/git-raw-commits": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-3.0.0.tgz", + "integrity": "sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==", + "deprecated": "This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead.", "dev": true, + "license": "MIT", "dependencies": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" + "dargs": "^7.0.0", + "meow": "^8.1.2", + "split2": "^3.2.2" }, "bin": { - "coveralls": "bin/coveralls.js" + "git-raw-commits": "cli.js" }, "engines": { - "node": ">=6" + "node": ">=14" } }, - "node_modules/cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "node_modules/conventional-recommended-bump/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, + "license": "ISC", "dependencies": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/cp-file/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/conventional-recommended-bump/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/cp-file/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/conventional-recommended-bump/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-inspect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", - "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", + "node_modules/conventional-recommended-bump/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.4.0" + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" }, "engines": { - "node": ">=16.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/conventional-recommended-bump/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/conventional-recommended-bump/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "p-try": "^2.0.0" }, "engines": { - "node": ">= 8" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "node_modules/conventional-recommended-bump/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/cssfilter": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", - "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==", - "dev": true, - "license": "MIT" - }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "node_modules/conventional-recommended-bump/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, + "license": "MIT", "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" }, "engines": { - "node": ">=0.12" + "node": ">=8" } }, - "node_modules/dargs": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", - "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "node_modules/conventional-recommended-bump/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==", + "node_modules/conventional-recommended-bump/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "ms": "2.0.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/debug-fabulous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "dependencies": { - "debug": "3.X", - "memoizee": "0.4.X", - "object-assign": "4.X" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/debug-fabulous/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/conventional-recommended-bump/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" } }, - "node_modules/debug-fabulous/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "node_modules/conventional-recommended-bump/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "node_modules/conventional-recommended-bump/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, + "readable-stream": "^3.0.0" + } + }, + "node_modules/conventional-recommended-bump/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=0.10.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "node_modules/conventional-recommended-bump/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "MIT", + "license": "ISC" + }, + "node_modules/conventional-recommended-bump/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">= 0.6" } }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "engines": { + "node": ">=6.6.0" } }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", "dev": true, "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==", + "node_modules/copy-props": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-4.0.0.tgz", + "integrity": "sha512-bVWtw1wQLzzKiYROtvNlbJgxgBYt2bMJpkCbKmXM3xyijvcjjWXEk5nyrrT3bgJ7ODb19ZohE2T0Y3FgNPyoTw==", "dev": true, + "license": "MIT", "dependencies": { - "strip-bom": "^3.0.0" + "each-props": "^3.0.0", + "is-plain-object": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">= 10.13.0" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "object-assign": "^4", + "vary": "^1" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", + "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "jiti": "^2.6.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" } }, - "node_modules/define-property/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "node_modules/coveralls": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", + "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" + "js-yaml": "^3.13.1", + "lcov-parse": "^1.0.0", + "log-driver": "^1.2.7", + "minimist": "^1.2.5", + "request": "^2.88.2" + }, + "bin": { + "coveralls": "bin/coveralls.js" }, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/coveralls/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "engines": { - "node": ">=0.4.0" + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/delete-empty": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/delete-empty/-/delete-empty-3.0.0.tgz", - "integrity": "sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ==", + "node_modules/coveralls/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.0", - "minimist": "^1.2.0", - "path-starts-with": "^2.0.0", - "rimraf": "^2.6.2" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { - "delete-empty": "bin/cli.js" - }, - "engines": { - "node": ">=10" + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true, - "engines": { - "node": ">=0.10" - } + "license": "MIT" }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/cross-inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=16.0.0" } }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "ISC" - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=6" + "node": ">= 8" } }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" } }, - "node_modules/detect-indent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", - "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, "engines": { "node": ">=4" } }, - "node_modules/detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", "dev": true, "license": "ISC", "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.3.1" + "node": ">=8" } }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "license": "MIT", "dependencies": { - "is-obj": "^2.0.0" + "assert-plus": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "node_modules/dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" + "node": "*" } }, - "node_modules/dotenv-expand": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "dotenv": "^16.4.5" + "ms": "^2.1.3" }, "engines": { - "node": ">=12" + "node": ">=6.0" }, - "funding": { - "url": "https://dotenvx.com" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", + "node_modules/debug-fabulous/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { - "readable-stream": "~1.1.9" + "ms": "^2.1.1" } }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/duplexify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/duplexify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, + "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/duplexify/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/duplexify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/each-props": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-3.0.0.tgz", - "integrity": "sha512-IYf1hpuWrdzse/s/YJOrFmU15lyhSzxelNVAHTEG3DtP4QsLTWZUzcUL3HMXmKQxXpa4EIrBPpwRgj0aehdvAw==", + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true, "license": "MIT", - "dependencies": { - "is-plain-object": "^5.0.0", - "object.defaults": "^1.1.0" - }, "engines": { - "node": ">= 10.13.0" + "node": ">=0.10" } }, - "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/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, + "license": "MIT", "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/encodeurl": { + "node_modules/define-lazy-prop": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "iconv-lite": "^0.6.2" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "is-descriptor": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "dependencies": { - "once": "^1.4.0" + "license": "MIT", + "engines": { + "node": ">=0.4.0" } }, - "node_modules/engine.io": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", - "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "node_modules/delete-empty": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/delete-empty/-/delete-empty-3.0.0.tgz", + "integrity": "sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" + "ansi-colors": "^4.1.0", + "minimist": "^1.2.0", + "path-starts-with": "^2.0.0", + "rimraf": "^2.6.2" + }, + "bin": { + "delete-empty": "bin/cli.js" }, "engines": { - "node": ">=10.2.0" + "node": ">=10" } }, - "node_modules/engine.io-client": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", - "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.18.3", - "xmlhttprequest-ssl": "~2.1.1" + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" } }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.8" } }, - "node_modules/engine.io-client/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "dev": true, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "license": "MIT", "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">=6" } }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=0.10.0" } }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "node_modules/detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "^2.1.3" - }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" + "asap": "^2.0.0", + "wrappy": "1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "node_modules/diff": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "integrity": "sha512-9wfm3RLzMp/PyTFWuw9liEzdlxsdGixCW0ZTU1XDmtlAkvpVXTPGF8KnfSs0hm3BPbg19OrUPPsRkHXoREpP1g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=0.3.1" } }, - "node_modules/envinfo": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" + "dependencies": { + "is-obj": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">=18" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://dotenvx.com" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dotenv": "^16.4.5" + }, "engines": { - "node": ">= 0.4" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true, - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "license": "MIT" }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "node_modules/duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", "dev": true, + "license": "BSD", "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "readable-stream": "~1.1.9" } }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "node_modules/duplexer2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true, - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } + "license": "MIT" }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, + "license": "MIT", "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "license": "MIT" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, - "engines": { - "node": ">=0.8.0" + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" } }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "node_modules/each-props": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-3.0.0.tgz", + "integrity": "sha512-IYf1hpuWrdzse/s/YJOrFmU15lyhSzxelNVAHTEG3DtP4QsLTWZUzcUL3HMXmKQxXpa4EIrBPpwRgj0aehdvAw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "is-plain-object": "^5.0.0", + "object.defaults": "^1.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "node": ">= 10.13.0" } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "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, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } + "license": "MIT" }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", - "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.1", - "synckit": "^0.11.12" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, - "license": "BSD-2-Clause", + "license": "Apache-2.0", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "jake": "^10.8.5" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "bin": { + "ejs": "bin/cli.js" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/electron-to-chromium": { + "version": "1.5.330", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.330.tgz", + "integrity": "sha512-jFNydB5kFtYUobh4IkWUnXeyDbjf/r9gcUEXe1xcrcUxIGfTdzPXA+ld6zBRbwvgIGVzDll/LTIiDztEtckSnA==", "dev": true, - "license": "Apache-2.0", + "license": "ISC" + }, + "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, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 0.8" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "iconv-lite": "^0.6.2" } }, - "node_modules/eslint/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "ms": "^2.1.3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "once": "^1.4.0" } }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, + "node_modules/engine.io": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.6.tgz", + "integrity": "sha512-U2SN0w3OpjFRVlrc17E6TMDmH58Xl9rai1MblNjAdwWp07Kk+llmzX0hjDpQdrDGzwmvOtgM5yI+meYX6iZ2xA==", "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "@types/ws": "^8.5.12", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.2.0" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/engine.io-client": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", + "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3", + "xmlhttprequest-ssl": "~2.1.1" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { - "node": ">=10" + "node": ">=10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "mime-db": "1.52.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/eslint/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/eslint/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "node_modules/engine.io/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, + "license": "MIT", "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" + "ansi-colors": "^4.1.1" }, "engines": { - "node": ">=0.10" + "node": ">=8.6" } }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=6" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/envinfo": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", + "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", "dev": true, + "license": "MIT", "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "envinfo": "dist/cli.js" }, "engines": { "node": ">=4" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } + "license": "MIT" }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, - "engines": { - "node": ">=4.0" + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.4" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/event-stream": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", - "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, + "license": "MIT", "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-stream": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", + "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/eventsource": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.1.0.tgz", @@ -11013,9 +10348,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz", - "integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", "dev": true, "license": "MIT", "engines": { @@ -11046,24 +10381,12 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", "dev": true, + "license": "MIT", "dependencies": { "is-posix-bracket": "^0.1.0" }, @@ -11076,6 +10399,7 @@ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^2.1.0" }, @@ -11088,6 +10412,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^2.1.0", "isobject": "^2.0.0", @@ -11104,6 +10429,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", "dev": true, + "license": "MIT", "dependencies": { "kind-of": "^3.0.2" }, @@ -11111,17 +10437,12 @@ "node": ">=0.10.0" } }, - "node_modules/expand-range/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/expand-range/node_modules/isobject": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", "dev": true, + "license": "MIT", "dependencies": { "isarray": "1.0.0" }, @@ -11129,18 +10450,6 @@ "node": ">=0.10.0" } }, - "node_modules/expand-range/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -11154,6 +10463,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/exponential-backoff": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", @@ -11204,151 +10523,48 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express/node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, "license": "MIT", "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "node_modules/extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", + "dev": true, "license": "MIT", + "dependencies": { + "is-extglob": "^1.0.0" + }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/express/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/express/node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/express/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dev": true, - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "node": ">=0.10.0" } }, "node_modules/extglob/node_modules/is-extglob": { @@ -11356,6 +10572,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11367,13 +10584,15 @@ "dev": true, "engines": [ "node >=0.6.0" - ] + ], + "license": "MIT" }, "node_modules/fancy-log": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", "dev": true, + "license": "MIT", "dependencies": { "color-support": "^1.1.3" }, @@ -11385,19 +10604,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", - "dev": true + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, "node_modules/fast-fifo": { "version": "1.3.2", @@ -11411,6 +10624,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -11422,11 +10636,25 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-json-stringify": { "version": "6.3.0", @@ -11452,20 +10680,11 @@ "rfdc": "^1.2.0" } }, - "node_modules/fast-levenshtein": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", - "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", - "dev": true, - "dependencies": { - "fastest-levenshtein": "^1.0.7" - } - }, "node_modules/fast-querystring": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dev": true, + "license": "MIT", "dependencies": { "fast-decode-uri-component": "^1.0.1" } @@ -11473,16 +10692,17 @@ "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" }, "node_modules/fast-unique-numbers": { - "version": "9.0.22", - "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.22.tgz", - "integrity": "sha512-dBR+30yHAqBGvOuxxQdnn2lTLHCO6r/9B+M4yF8mNrzr3u1yiF+YVJ6u3GTyPN/VRWqaE1FcscZDdBgVKmrmQQ==", + "version": "9.0.27", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", + "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.27.6", + "@babel/runtime": "^7.29.2", "tslib": "^2.8.1" }, "engines": { @@ -11490,9 +10710,9 @@ } }, "node_modules/fast-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", - "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "funding": [ { "type": "github", @@ -11502,13 +10722,15 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.9.1" } @@ -11517,7 +10739,6 @@ "version": "5.7.4", "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.7.4.tgz", "integrity": "sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA==", - "dev": true, "funding": [ { "type": "github", @@ -11551,24 +10772,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz", "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/fastify/node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", - "dev": true, "funding": [ { "type": "github", @@ -11581,24 +10784,11 @@ ], "license": "MIT" }, - "node_modules/fastify/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -11647,16 +10837,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "flat-cache": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=16.0.0" + "node": ">=0.8.0" } }, "node_modules/file-type": { @@ -11682,12 +10870,13 @@ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -11695,9 +10884,9 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -11705,9 +10894,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "license": "ISC", "dependencies": { @@ -11722,6 +10911,7 @@ "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11731,6 +10921,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -11739,9 +10930,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -11752,73 +10943,17 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "node": ">= 18.0.0" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/find-my-way": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.4.0.tgz", "integrity": "sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -11867,6 +11002,7 @@ "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", "integrity": "sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "^2.0.2" }, @@ -11874,42 +11010,6 @@ "node": ">=0.10.0" } }, - "node_modules/first-chunk-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/first-chunk-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/first-chunk-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/first-chunk-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/flagged-respawn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-2.0.0.tgz", @@ -11925,75 +11025,22 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true - }, "node_modules/flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "readable-stream": "^2.3.6" } }, - "node_modules/flush-write-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/flush-write-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/flush-write-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/flush-write-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -12036,6 +11083,7 @@ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12075,6 +11123,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -12087,6 +11136,7 @@ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "*" } @@ -12108,6 +11158,29 @@ "node": ">= 6" } }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/formidable": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", @@ -12130,6 +11203,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -12139,6 +11213,7 @@ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", "dev": true, + "license": "MIT", "dependencies": { "map-cache": "^0.2.2" }, @@ -12150,6 +11225,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -12158,7 +11234,8 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/front-matter": { "version": "4.0.2", @@ -12170,6 +11247,30 @@ "js-yaml": "^3.13.1" } }, + "node_modules/front-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/front-matter/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -12178,9 +11279,9 @@ "license": "MIT" }, "node_modules/fs-extra": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", - "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", "dev": true, "license": "MIT", "dependencies": { @@ -12223,7 +11324,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -12231,6 +11333,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -12243,6 +11346,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12252,23 +11356,35 @@ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", "dev": true, + "license": "MIT", "dependencies": { "is-property": "^1.0.2" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "dev": true, "license": "MIT", "engines": { @@ -12278,15 +11394,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -12368,6 +11475,31 @@ "node": ">=10" } }, + "node_modules/get-pkg-repo/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/get-pkg-repo/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/get-pkg-repo/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -12436,11 +11568,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12450,26 +11596,26 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" } }, "node_modules/git-raw-commits": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", - "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz", + "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==", "dev": true, "license": "MIT", "dependencies": { - "dargs": "^8.0.0", - "meow": "^12.0.1", - "split2": "^4.0.0" + "@conventional-changelog/git-client": "^2.6.0", + "meow": "^13.0.0" }, "bin": { - "git-raw-commits": "cli.mjs" + "git-raw-commits": "src/cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/git-remote-origin-url": { @@ -12486,10 +11632,21 @@ "node": ">=4" } }, + "node_modules/git-remote-origin-url/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/git-semver-tags": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-5.0.1.tgz", "integrity": "sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==", + "deprecated": "This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead.", "dev": true, "license": "MIT", "dependencies": { @@ -12627,16 +11784,6 @@ "node": ">=8" } }, - "node_modules/git-semver-tags/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/git-semver-tags/node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -12721,19 +11868,6 @@ "node": ">=8" } }, - "node_modules/git-semver-tags/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/git-semver-tags/node_modules/type-fest": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", @@ -12747,6 +11881,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/git-semver-tags/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/git-semver-tags/node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", @@ -12796,21 +11937,18 @@ "license": "ISC" }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -12821,6 +11959,7 @@ "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", "dev": true, + "license": "MIT", "dependencies": { "glob-parent": "^2.0.0", "is-glob": "^2.0.0" @@ -12834,6 +11973,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^2.0.0" } @@ -12843,6 +11983,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12852,6 +11993,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^1.0.0" }, @@ -12860,15 +12002,16 @@ } }, "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==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/glob-stream": { @@ -12891,17 +12034,18 @@ "node": ">=10.13.0" } }, - "node_modules/glob-stream/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/glob-stream/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", "dependencies": { - "is-glob": "^4.0.3" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=10.13.0" + "node": ">= 8" } }, "node_modules/glob-watcher": { @@ -12909,6 +12053,7 @@ "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-6.0.0.tgz", "integrity": "sha512-wGM28Ehmcnk2NqRORXFOTOR064L4imSw3EeOqU5bIwUf62eXGwg89WivH6VMahL8zlQHeodzvHpXplrqzrz3Nw==", "dev": true, + "license": "MIT", "dependencies": { "async-done": "^2.0.0", "chokidar": "^3.5.3" @@ -12917,11 +12062,26 @@ "node": ">= 10.13.0" } }, - "node_modules/glob-watcher/node_modules/async-done": { - "version": "2.0.0", + "node_modules/glob-watcher/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/glob-watcher/node_modules/async-done": { + "version": "2.0.0", "resolved": "https://registry.npmjs.org/async-done/-/async-done-2.0.0.tgz", "integrity": "sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw==", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.4.4", "once": "^1.4.0", @@ -12936,6 +12096,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -12955,17 +12116,17 @@ "fsevents": "~2.3.2" } }, - "node_modules/glob-watcher/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/glob-watcher/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, - "license": "MIT", - "engines": { - "node": ">=8.6" + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">= 6" } }, "node_modules/glob-watcher/node_modules/readdirp": { @@ -12973,6 +12134,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -12980,6 +12142,45 @@ "node": ">=8.10.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/global-directory": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", @@ -13035,17 +12236,17 @@ "dev": true, "license": "ISC" }, - "node_modules/globals": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", - "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "which": "bin/which" } }, "node_modules/glogg": { @@ -13077,7 +12278,8 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/graphql": { "version": "16.12.0", @@ -13094,6 +12296,7 @@ "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-3.0.0.tgz", "integrity": "sha512-kZCdevgmzDjGAOqH7GlDmQXYAkuHoKpMlJrqF40HMPhUhM5ZWSFSxCwD/nSi6AkaijmMfsFhoJRGJ27UseCvRA==", "dev": true, + "license": "MIT", "peerDependencies": { "graphql": "^15.7.2 || ^16.0.0" } @@ -13103,6 +12306,7 @@ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.1.0" }, @@ -13164,6 +12368,7 @@ "resolved": "https://registry.npmjs.org/gulp-clang-format/-/gulp-clang-format-1.0.27.tgz", "integrity": "sha512-Jj4PGuNXKdqVCh9fijvL7wdzma5TQRJz1vv8FjOjnSkfq3s/mvbdE/jq+5HG1c/q+jcYkXTEGkYT3CrdnJOLaQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "clang-format": "^1.0.32", "fancy-log": "^1.3.2", @@ -13178,6 +12383,7 @@ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, + "license": "MIT", "dependencies": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", @@ -13193,6 +12399,7 @@ "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.4.0.tgz", "integrity": "sha512-DARK8rNMo4lHOFLGTiHEJdf19GuoBDHqGUaypz+fOhrvOs3iFO7ntdYtdpNxv+AzSJBx/JfypF0yEj9ks1IStQ==", "dev": true, + "license": "MIT", "dependencies": { "fancy-log": "^1.3.2", "plugin-error": "^0.1.2", @@ -13209,6 +12416,7 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", "dev": true, + "license": "MIT", "dependencies": { "arr-flatten": "^1.0.1", "array-slice": "^0.2.3" @@ -13222,6 +12430,7 @@ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13231,24 +12440,17 @@ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-clean/node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/gulp-clean/node_modules/extend-shallow": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", "dev": true, + "license": "MIT", "dependencies": { "kind-of": "^1.1.0" }, @@ -13261,6 +12463,7 @@ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, + "license": "MIT", "dependencies": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", @@ -13271,11 +12474,22 @@ "node": ">= 0.10" } }, + "node_modules/gulp-clean/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-clean/node_modules/plugin-error": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", "dev": true, + "license": "MIT", "dependencies": { "ansi-cyan": "^0.1.1", "ansi-red": "^0.1.1", @@ -13287,32 +12501,6 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-clean/node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-clean/node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, - "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/gulp-cli": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.1.0.tgz", @@ -13352,6 +12540,24 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/gulp-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/gulp-cli/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -13386,6 +12592,7 @@ "resolved": "https://registry.npmjs.org/gulp-diff/-/gulp-diff-1.0.0.tgz", "integrity": "sha512-d4dwx2A1dzPTUS9kWl8WaWRhXshZHoQqbEy7x5tzDqN4V2C3dLh82BH7Plr10vjYtiqd+LitU0sX2f61mex3cQ==", "dev": true, + "license": "MIT", "dependencies": { "cli-color": "^1.0.0", "diff": "^2.0.2", @@ -13394,20 +12601,12 @@ "through2": "^2.0.0" } }, - "node_modules/gulp-diff/node_modules/diff": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", - "integrity": "sha512-9wfm3RLzMp/PyTFWuw9liEzdlxsdGixCW0ZTU1XDmtlAkvpVXTPGF8KnfSs0hm3BPbg19OrUPPsRkHXoREpP1g==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/gulp-sourcemaps": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", "dev": true, + "license": "ISC", "dependencies": { "@gulp-sourcemaps/identity-map": "^2.0.1", "@gulp-sourcemaps/map-sources": "^1.0.0", @@ -13430,6 +12629,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -13441,13 +12641,15 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/gulp-typescript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-5.0.1.tgz", "integrity": "sha512-YuMMlylyJtUSHG1/wuSVTrZp60k1dMEFKYOvDf7OvbAJWrDtxxD4oZon4ancdWwzjj30ztiidhe4VXJniF0pIQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "^3.0.5", "plugin-error": "^1.0.1", @@ -13468,30 +12670,24 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/gulp-typescript/node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/gulp-typescript/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/gulp-typescript/node_modules/fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.11", "through2": "^2.0.3" @@ -13500,51 +12696,45 @@ "node": ">= 0.10" } }, - "node_modules/gulp-typescript/node_modules/fs-mkdirp-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-typescript/node_modules/fs-mkdirp-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/gulp-typescript/node_modules/fs-mkdirp-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/gulp-typescript/node_modules/fs-mkdirp-stream/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, + "node_modules/gulp-typescript/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/gulp-typescript/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^3.1.0", "path-dirname": "^1.0.0" @@ -13555,6 +12745,7 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", "dev": true, + "license": "MIT", "dependencies": { "extend": "^3.0.0", "glob": "^7.1.1", @@ -13571,41 +12762,12 @@ "node": ">= 0.10" } }, - "node_modules/gulp-typescript/node_modules/glob-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-typescript/node_modules/glob-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/gulp-typescript/node_modules/glob-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/gulp-typescript/node_modules/is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.0" }, @@ -13613,17 +12775,12 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-typescript/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/gulp-typescript/node_modules/lead": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", "dev": true, + "license": "MIT", "dependencies": { "flush-write-stream": "^1.0.2" }, @@ -13636,6 +12793,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, + "license": "MIT", "dependencies": { "remove-trailing-separator": "^1.0.1" }, @@ -13648,6 +12806,7 @@ "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.3.2" }, @@ -13655,34 +12814,12 @@ "node": ">= 0.10" } }, - "node_modules/gulp-typescript/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gulp-typescript/node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/gulp-typescript/node_modules/resolve-options": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", "dev": true, + "license": "MIT", "dependencies": { "value-or-function": "^3.0.0" }, @@ -13691,21 +12828,13 @@ } }, "node_modules/gulp-typescript/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">= 8" - } - }, - "node_modules/gulp-typescript/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">= 12" } }, "node_modules/gulp-typescript/node_modules/through2": { @@ -13713,6 +12842,7 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.4", "readable-stream": "2 || 3" @@ -13723,6 +12853,7 @@ "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", "dev": true, + "license": "MIT", "dependencies": { "through2": "^2.0.3" }, @@ -13730,41 +12861,12 @@ "node": ">= 0.10" } }, - "node_modules/gulp-typescript/node_modules/to-through/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-typescript/node_modules/to-through/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/gulp-typescript/node_modules/to-through/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/gulp-typescript/node_modules/to-through/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -13775,23 +12877,7 @@ "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-typescript/node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, - "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -13801,6 +12887,7 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", "dev": true, + "license": "MIT", "dependencies": { "fs-mkdirp-stream": "^1.0.0", "glob-stream": "^6.1.0", @@ -13824,41 +12911,12 @@ "node": ">= 0.10" } }, - "node_modules/gulp-typescript/node_modules/vinyl-fs/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-typescript/node_modules/vinyl-fs/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/gulp-typescript/node_modules/vinyl-fs/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/gulp-typescript/node_modules/vinyl-fs/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -13869,6 +12927,7 @@ "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", "dev": true, + "license": "MIT", "dependencies": { "append-buffer": "^1.0.2", "convert-source-map": "^1.5.0", @@ -13888,6 +12947,7 @@ "integrity": "sha512-q5oWPc12lwSFS9h/4VIjG+1NuNDlJ48ywV2JKItY4Ycc/n1fXJeYPVQsfu5ZrhQi7FGSDBalwUCLar/GyHXKGw==", "deprecated": "gulp-util is deprecated - replace it, following the guidelines at https://medium.com/gulpjs/gulp-util-ca3b1f9f9ac5", "dev": true, + "license": "MIT", "dependencies": { "array-differ": "^1.0.0", "array-uniq": "^1.0.2", @@ -13912,20 +12972,12 @@ "node": ">=0.10" } }, - "node_modules/gulp-util/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/gulp-util/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13935,6 +12987,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -13946,17 +12999,39 @@ "node": ">=0.10.0" } }, + "node_modules/gulp-util/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/gulp-util/node_modules/clone-stats": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-util/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } }, "node_modules/gulp-util/node_modules/fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, + "license": "MIT", "dependencies": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", @@ -13972,6 +13047,7 @@ "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, + "license": "MIT", "dependencies": { "sparkles": "^1.0.0" }, @@ -13984,6 +13060,7 @@ "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", "dev": true, + "license": "MIT", "dependencies": { "glogg": "^1.0.0" }, @@ -13996,24 +13073,17 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-util/node_modules/replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/gulp-util/node_modules/sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -14023,6 +13093,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" }, @@ -14035,6 +13106,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -14044,6 +13116,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", "integrity": "sha512-P5zdf3WB9uzr7IFoVQ2wZTmUwHL8cMZWJGzLBNCHNZ3NB6HTMsYABtt7z8tAGIINLXyAob9B9a1yzVGMFOYKEA==", "dev": true, + "license": "MIT", "dependencies": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -14058,6 +13131,7 @@ "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", "integrity": "sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "1.1.0", "anymatch": "^1.3.0", @@ -14078,6 +13152,7 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-wrap": "^0.1.0" }, @@ -14085,24 +13160,12 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "dependencies": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "node_modules/gulp-watch/node_modules/arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", + "node_modules/gulp-watch/node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", "dev": true, - "dependencies": { - "arr-flatten": "^1.0.1" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -14112,19 +13175,28 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/gulp-watch/node_modules/braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, + "license": "MIT", "dependencies": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "engines": { "node": ">=0.10.0" @@ -14135,6 +13207,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, + "license": "MIT", "dependencies": { "anymatch": "^2.0.0", "async-each": "^1.0.1", @@ -14157,6 +13230,7 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, + "license": "ISC", "dependencies": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" @@ -14167,6 +13241,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, + "license": "MIT", "dependencies": { "remove-trailing-separator": "^1.0.1" }, @@ -14174,62 +13249,36 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/gulp-watch/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" + "ms": "2.0.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/gulp-watch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/expand-brackets": { + "node_modules/gulp-watch/node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -14243,11 +13292,12 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/expand-brackets/node_modules/define-property": { + "node_modules/gulp-watch/node_modules/expand-brackets/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, + "license": "MIT", "dependencies": { "is-descriptor": "^0.1.0" }, @@ -14255,36 +13305,39 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/gulp-watch/node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dev": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "node_modules/gulp-watch/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, + "license": "MIT", "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/extglob": { + "node_modules/gulp-watch/node_modules/extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, + "license": "MIT", "dependencies": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -14299,11 +13352,12 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/extglob/node_modules/define-property": { + "node_modules/gulp-watch/node_modules/extglob/node_modules/define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, + "license": "MIT", "dependencies": { "is-descriptor": "^1.0.0" }, @@ -14311,95 +13365,12 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/chokidar/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/gulp-watch/node_modules/chokidar/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/gulp-watch/node_modules/chokidar/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/chokidar/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/chokidar/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/gulp-watch/node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/gulp-watch/node_modules/fancy-log": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", "integrity": "sha512-7E6IFy84FpO6jcnzEsCcoxDleHpMTFzncmCXXBIVYq1/Oakqnbc/lTKPJyyW6edGeC/rnZmV78hJe7SuoZo0aQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", @@ -14414,6 +13385,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", "dev": true, + "license": "MIT", "dependencies": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -14424,18 +13396,6 @@ "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/gulp-watch/node_modules/fsevents": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", @@ -14443,6 +13403,7 @@ "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -14460,25 +13421,18 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^3.1.0", "path-dirname": "^1.0.0" } }, - "node_modules/gulp-watch/node_modules/glob-parent/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/gulp-watch/node_modules/glob-parent/node_modules/is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.0" }, @@ -14491,6 +13445,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^1.0.0" }, @@ -14503,6 +13458,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, + "license": "MIT", "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" @@ -14516,117 +13472,112 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "node_modules/gulp-watch/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "node_modules/gulp-watch/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "license": "MIT", "dependencies": { - "is-extglob": "^1.0.0" + "isobject": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/gulp-watch/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/gulp-watch/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/gulp-watch/node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "dev": true, + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", + "node_modules/gulp-watch/node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, + "license": "MIT", "dependencies": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "is-plain-object": "^2.0.4" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/gulp-watch/node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/gulp-watch/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "license": "MIT" }, "node_modules/gulp-watch/node_modules/readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", @@ -14636,240 +13587,29 @@ "node": ">=0.10" } }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "node_modules/gulp-watch/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "node_modules/gulp-watch/node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/readdirp/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-watch/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/gulp-watch/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/gulp-watch/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-watch/node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, - "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" + "node": ">=4", + "yarn": "*" } }, "node_modules/gulplog": { @@ -14886,10 +13626,11 @@ } }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", @@ -14911,6 +13652,7 @@ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "dev": true, + "license": "ISC", "engines": { "node": ">=4" } @@ -14921,6 +13663,7 @@ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "deprecated": "this library is no longer supported", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -14930,10 +13673,11 @@ } }, "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14949,7 +13693,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/hard-rejection": { "version": "2.1.0", @@ -14966,6 +13711,7 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" }, @@ -14973,20 +13719,12 @@ "node": ">=0.10.0" } }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -14996,6 +13734,7 @@ "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", "integrity": "sha512-+F4GzLjwHNNDEAJW2DC1xXfEoPkRDmUdJ7CBYw4MpqtDwOnqdImJl7GWlpqx+Wko6//J8uKTnIe4wZSv7yCqmw==", "dev": true, + "license": "MIT", "dependencies": { "sparkles": "^1.0.0" }, @@ -15008,6 +13747,7 @@ "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -15017,6 +13757,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -15041,6 +13782,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -15063,6 +13805,7 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", "dev": true, + "license": "MIT", "dependencies": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -15077,6 +13820,7 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -15090,6 +13834,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", "dev": true, + "license": "MIT", "dependencies": { "kind-of": "^3.0.2" }, @@ -15102,6 +13847,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, + "license": "MIT", "dependencies": { "is-buffer": "^1.1.5" }, @@ -15114,6 +13860,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", "dev": true, + "license": "MIT", "dependencies": { "is-buffer": "^1.1.5" }, @@ -15121,26 +13868,14 @@ "node": ">=0.10.0" } }, - "node_modules/hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha512-w0Kz8lJFBoyaurBiNrIvxPqr/gJ6fOfSkpAPOepN3oECqGJag37xPbOv57izi/KP8auHgNYxn5fXtAb+1LsJ6w==", - "dev": true, - "dependencies": { - "is-stream": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/hashery": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.3.0.tgz", - "integrity": "sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", "dev": true, "license": "MIT", "dependencies": { - "hookified": "^1.13.0" + "hookified": "^1.15.0" }, "engines": { "node": ">=20" @@ -15150,6 +13885,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -15157,26 +13893,19 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/help-me": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": "*" } @@ -15195,30 +13924,38 @@ } }, "node_modules/hookified": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.13.0.tgz", - "integrity": "sha512-6sPYUY8olshgM/1LDNW4QZQN0IqgKhtl/1C8koNZBJrKLBk3AZl6chQtNwpNztvfiApHMEwMHek5rv993PRbWw==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", "dev": true, "license": "MIT" }, "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", "dev": true, - "license": "ISC" - }, - "node_modules/html-escaper": { - "version": "2.0.2", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-errors": { "version": "2.0.1", @@ -15254,46 +13991,12 @@ "node": ">= 14" } }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -15305,46 +14008,25 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 14" } }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "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, + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } @@ -15354,6 +14036,7 @@ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.0.0" } @@ -15363,6 +14046,7 @@ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, + "license": "MIT", "bin": { "husky": "bin.js" }, @@ -15406,16 +14090,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "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" - } + ], + "license": "BSD-3-Clause" }, "node_modules/ignore-walk": { "version": "8.0.0", @@ -15430,27 +14106,51 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/ignore-walk/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/ignore-walk/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -15467,6 +14167,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -15491,85 +14192,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/import-meta-resolve": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", @@ -15586,6 +14208,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -15595,6 +14218,7 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -15603,7 +14227,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/inflight": { "version": "1.0.6", @@ -15611,6 +14236,7 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -15619,7 +14245,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ini": { "version": "4.1.1", @@ -15650,19 +14277,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/init-package-json/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/inquirer": { "version": "12.9.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.9.6.tgz", @@ -15725,62 +14339,23 @@ "url": "https://opencollective.com/ioredis" } }, - "node_modules/ioredis/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/ioredis/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, + "license": "MIT", "engines": { "node": ">= 12" } }, - "node_modules/ip-address/node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true - }, "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10" } }, "node_modules/is-absolute": { @@ -15788,6 +14363,7 @@ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, + "license": "MIT", "dependencies": { "is-relative": "^1.0.0", "is-windows": "^1.0.1" @@ -15801,6 +14377,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.0" }, @@ -15812,13 +14389,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -15830,13 +14409,15 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -15857,26 +14438,10 @@ "is-ci": "bin.js" } }, - "node_modules/is-ci/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -15894,6 +14459,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.0" }, @@ -15906,6 +14472,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dev": true, + "license": "MIT", "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" @@ -15935,6 +14502,7 @@ "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15944,6 +14512,7 @@ "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", "dev": true, + "license": "MIT", "dependencies": { "is-primitive": "^2.0.0" }, @@ -15956,6 +14525,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4" }, @@ -15968,6 +14538,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -15980,17 +14551,25 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-glob": { @@ -15998,6 +14577,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -16010,6 +14590,7 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -16018,13 +14599,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16034,6 +14617,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -16048,24 +14632,17 @@ "node": ">=8" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-plain-object": { @@ -16073,6 +14650,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16082,6 +14660,7 @@ "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16091,6 +14670,7 @@ "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16099,19 +14679,22 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, + "license": "MIT", "dependencies": { "is-unc-path": "^1.0.0" }, @@ -16130,10 +14713,24 @@ } }, "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } @@ -16158,13 +14755,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, + "license": "MIT", "dependencies": { "unc-path-regex": "^0.1.2" }, @@ -16177,6 +14776,7 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -16188,13 +14788,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-valid-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16204,6 +14806,7 @@ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16222,22 +14825,25 @@ } }, "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16246,153 +14852,91 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "node_modules/istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "dependencies": { - "append-transform": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", "dev": true, - "dependencies": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/istanbul-lib-instrument/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": ">=8" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" + "has-flag": "^4.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/istanbul-lib-source-maps/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/istanbul-lib-source-maps/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/iterall": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/iterare": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", "engines": { "node": ">=6" } }, "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==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/jake": { @@ -16413,24 +14957,17 @@ "node": ">=10" } }, - "node_modules/jake/node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.0.1", + "@jest/diff-sequences": "30.3.0", "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "pretty-format": "30.2.0" + "pretty-format": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -16451,6 +14988,7 @@ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/js-sdsl" @@ -16460,16 +14998,17 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -16479,13 +15018,15 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -16493,30 +15034,29 @@ "node": ">=6" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-schema-ref-resolver": { "version": "3.0.0", @@ -16540,13 +15080,15 @@ "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-nice": { "version": "1.1.4", @@ -16562,13 +15104,15 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -16584,10 +15128,11 @@ "license": "MIT" }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -16602,13 +15147,15 @@ "dev": true, "engines": [ "node >= 0.2.0" - ] + ], + "license": "MIT" }, "node_modules/JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, + "license": "(MIT OR Apache-2.0)", "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -16625,6 +15172,7 @@ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -16654,14 +15202,15 @@ "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-2.2.4.tgz", "integrity": "sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/kareem": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.2.0.tgz", - "integrity": "sha512-VS8MWZz/cT+SqBCpVfNN4zoVz5VskR3N4+sTmUXme55e9avQHntpwpNq0yjnosISXqwJ3AQVjlbI4Dyzv//JtA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.0.0.tgz", + "integrity": "sha512-RKhaOBSPN8L7y4yAgNhDT2602G5FD6QbOIISbjN9D6mjHPeqeg7K+EB5IGSU5o81/X2Gzm3ICnAvQW3x3OP8HA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -16669,19 +15218,24 @@ } }, "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, + "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "@keyv/serialize": "^1.1.1" } }, "node_modules/kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, "engines": { "node": ">=0.10.0" } @@ -16691,6 +15245,7 @@ "resolved": "https://registry.npmjs.org/last-run/-/last-run-2.0.0.tgz", "integrity": "sha512-j+y6WhTLN4Itnf9j5ZQos1BGPCS8DAwmgMroR3OzfxAsBxam0hMw7J8M3KqZl0pLQJ1jNnwIexg5DYpC/ctwEQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.13.0" } @@ -16700,6 +15255,7 @@ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "^2.0.5" }, @@ -16707,47 +15263,12 @@ "node": ">= 0.6.3" } }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/lcov-parse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "lcov-parse": "bin/cli.js" } @@ -16859,6 +15380,7 @@ "resolved": "https://registry.npmjs.org/lerna-changelog/-/lerna-changelog-2.2.0.tgz", "integrity": "sha512-yjYNAHrbnw8xYFKmYWJEP52Tk4xSdlNmzpYr26+3glbSGDmpe8UMo8f9DlEntjGufL+opup421oVTXcLshwAaQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.0.0", "cli-highlight": "^2.1.11", @@ -16881,16 +15403,31 @@ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "dev": true, + "license": "ISC", "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" } }, + "node_modules/lerna-changelog/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/lerna-changelog/node_modules/cacache": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", @@ -16920,6 +15457,7 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, + "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -16930,44 +15468,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lerna-changelog/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/lerna-changelog/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/lerna-changelog/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "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" - }, + "license": "ISC", "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/lerna-changelog/node_modules/fs-minipass": { @@ -16975,6 +15483,7 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -16982,16 +15491,26 @@ "node": ">= 8" } }, - "node_modules/lerna-changelog/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==", + "node_modules/lerna-changelog/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/lerna-changelog/node_modules/hosted-git-info": { @@ -16999,6 +15518,7 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -17011,6 +15531,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, + "license": "MIT", "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -17020,16 +15541,18 @@ "node": ">= 6" } }, - "node_modules/lerna-changelog/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==", + "node_modules/lerna-changelog/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 6" } }, "node_modules/lerna-changelog/node_modules/lru-cache": { @@ -17037,6 +15560,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -17049,6 +15573,7 @@ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", "dev": true, + "license": "ISC", "dependencies": { "agentkeepalive": "^4.1.3", "cacache": "^15.2.0", @@ -17076,6 +15601,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -17088,6 +15614,7 @@ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -17100,6 +15627,7 @@ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", "dev": true, + "license": "MIT", "dependencies": { "minipass": "^3.1.0", "minipass-sized": "^1.0.3", @@ -17112,11 +15640,26 @@ "encoding": "^0.1.12" } }, + "node_modules/lerna-changelog/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/lerna-changelog/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -17124,17 +15667,22 @@ "node": ">=10" } }, - "node_modules/lerna-changelog/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/lerna-changelog/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, "node_modules/lerna-changelog/node_modules/p-map": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, + "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -17148,6 +15696,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -17158,23 +15707,12 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lerna-changelog/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/lerna-changelog/node_modules/socks-proxy-agent": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", @@ -17189,6 +15727,7 @@ "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.1.1" }, @@ -17196,30 +15735,41 @@ "node": ">= 8" } }, - "node_modules/lerna-changelog/node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "node_modules/lerna-changelog/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { - "unique-slug": "^2.0.0" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/lerna-changelog/node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "node_modules/lerna-changelog/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" + "license": "ISC", + "engines": { + "node": ">=8" } }, - "node_modules/lerna/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/lerna-changelog/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "Python-2.0" + "license": "ISC" }, "node_modules/lerna/node_modules/chalk": { "version": "4.1.0", @@ -17238,61 +15788,44 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/lerna/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "node_modules/lerna/node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, "engines": { - "node": ">=18" + "node": ">=16" } }, - "node_modules/lerna/node_modules/glob": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.2.tgz", - "integrity": "sha512-035InabNu/c1lW0tzPhAgapKctblppqsKKG9ZaNzbr+gXwWMjXoiyGSyB9sArzrjG7jY+zntRq5ZSUYemrnWVQ==", + "node_modules/lerna/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "minimatch": "^10.1.2", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, "engines": { - "node": "20 || >=22" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lerna/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/lerna/node_modules/glob/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/lerna/node_modules/ini": { @@ -17302,45 +15835,6 @@ "dev": true, "license": "ISC" }, - "node_modules/lerna/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lerna/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/lerna/node_modules/load-json-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", - "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^5.0.0", - "strip-bom": "^4.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/lerna/node_modules/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", @@ -17354,57 +15848,27 @@ "node": "*" } }, - "node_modules/lerna/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/lerna/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lerna/node_modules/pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "node_modules/lerna/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/lerna/node_modules/rimraf": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", - "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "glob": "^13.0.0", + "glob": "^13.0.3", "package-json-from-dist": "^1.0.1" }, "bin": { @@ -17440,31 +15904,17 @@ "node": ">=8" } }, - "node_modules/lerna/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "node_modules/lerna/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lerna/node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", - "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=8" } }, "node_modules/lerna/node_modules/tinyglobby": { @@ -17484,15 +15934,18 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/lerna/node_modules/upath": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", - "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "node_modules/lerna/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=4", - "yarn": "*" + "node": ">=14.17" } }, "node_modules/lerna/node_modules/uuid": { @@ -17509,56 +15962,6 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/lerna/node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/lerna/node_modules/write-file-atomic/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, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lerna/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/libnpmaccess": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-10.0.3.tgz", @@ -17593,23 +15996,27 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/libnpmpublish/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/libnpmpublish/node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" } }, "node_modules/libphonenumber-js": { - "version": "1.11.15", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.15.tgz", - "integrity": "sha512-M7+rtYi9l5RvMmHyjyoF3BHHUpXTYdJ0PezZGHNs0GyW1lO+K7jxlXpbdIb7a56h0nqLYdjIw+E+z0ciGaJP7g==" + "version": "1.12.41", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.41.tgz", + "integrity": "sha512-lsmMmGXBxXIK/VMLEj0kL6MtUs1kBGj1nTCzi6zgQoG1DEwqwt2DQyHxcLykceIxAnfE3hya7NuIh6PpC6S3fA==", + "license": "MIT" }, "node_modules/liftoff": { "version": "5.0.1", @@ -17634,7 +16041,6 @@ "version": "6.6.0", "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", - "dev": true, "funding": [ { "type": "github", @@ -17652,52 +16058,335 @@ "set-cookie-parser": "^2.6.0" } }, - "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/light-my-request/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/light-my-request/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, - "node_modules/lint-staged": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", - "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, - "license": "MIT", + "license": "MPL-2.0", "dependencies": { - "commander": "^14.0.2", - "listr2": "^9.0.5", - "micromatch": "^4.0.8", - "nano-spawn": "^2.0.0", - "pidtree": "^0.6.0", - "string-argv": "^0.3.2", - "yaml": "^2.8.1" + "detect-libc": "^2.0.3" }, - "bin": { - "lint-staged": "bin/lint-staged.js" + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=20.17" + "node": ">= 12.0.0" }, "funding": { - "url": "https://opencollective.com/lint-staged" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/lint-staged/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=20" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/listr2": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", - "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lint-staged": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", "dependencies": { @@ -17745,13 +16434,6 @@ "dev": true, "license": "MIT" }, - "node_modules/listr2/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true, - "license": "MIT" - }, "node_modules/listr2/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -17771,13 +16453,13 @@ } }, "node_modules/listr2/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -17824,40 +16506,19 @@ } }, "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", + "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", "dev": true, + "license": "MIT", "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/lodash": { @@ -17871,94 +16532,102 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._basetostring": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", "integrity": "sha512-mTzAr1aNAv/i7W43vOR/uD/aJ4ngbtsRaCubp2BfZhlGU/eORUjg/7F6X0orNMdv33JOrdgGybtvMN/po3EWrA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._basevalues": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", "integrity": "sha512-H94wl5P13uEqlCg7OcNNhMQ8KvWSIyqXzOPusRgHC9DK3o54P6P3xtbXlVbRABG4q5gSmp7EDdJ0MSuW9HX6Mg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._getnative": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._isiterateecall": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._reescape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", "integrity": "sha512-Sjlavm5y+FUVIF3vF3B75GyXrzsfYV8Dlv3L4mEpuB9leg8N6yf/7rU06iLPx9fY0Mv3khVp9p7Dx0mGV6V5OQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._reevaluate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", "integrity": "sha512-OrPwdDc65iJiBeUe5n/LIjd7Viy99bKwDdk7Z5ljfZg0uFRFlfQaCy9tZ4YMAag9WAZmlVpe1iZrkIMMSMHD3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash._root": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.escape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", "integrity": "sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==", "dev": true, + "license": "MIT", "dependencies": { "lodash._root": "^3.0.0" } }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true - }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isarray": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.ismatch": { "version": "4.4.0", @@ -17979,18 +16648,13 @@ "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", "integrity": "sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==", "dev": true, + "license": "MIT", "dependencies": { "lodash._getnative": "^3.0.0", "lodash.isarguments": "^3.0.0", "lodash.isarray": "^3.0.0" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, "node_modules/lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", @@ -18002,13 +16666,16 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "dev": true + "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.", + "dev": true, + "license": "MIT" }, "node_modules/lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", "integrity": "sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.snakecase": { "version": "4.1.1", @@ -18021,7 +16688,8 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.startcase": { "version": "4.4.0", @@ -18034,7 +16702,9 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", "integrity": "sha512-0B4Y53I0OgHUJkt+7RmlDFWKjVAI/YUpWNiL9GQz5ORDr4ttgfQGo+phBWKFLJbBdtOwgMuUkdOHOnPg45jKmQ==", + "deprecated": "This package is deprecated. Use https://socket.dev/npm/package/eta instead.", "dev": true, + "license": "MIT", "dependencies": { "lodash._basecopy": "^3.0.0", "lodash._basetostring": "^3.0.0", @@ -18052,6 +16722,7 @@ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", "integrity": "sha512-TcrlEr31tDYnWkHFWDCV3dHYroKEXpJZ2YJYvJdhN+y4AkWMDZ5I4I8XDtUKqSAyG81N7w+I1mFEJtcED+tGqQ==", "dev": true, + "license": "MIT", "dependencies": { "lodash._reinterpolate": "^3.0.0", "lodash.escape": "^3.0.0" @@ -18069,38 +16740,40 @@ "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", "dev": true, + "license": "ISC", "engines": { "node": ">=0.8.6" } }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=18" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz", - "integrity": "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==", + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, "license": "MIT", "dependencies": { - "environment": "^1.0.0" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { "node": ">=18" @@ -18142,6 +16815,23 @@ "dev": true, "license": "MIT" }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/log-update/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -18161,13 +16851,13 @@ } }, "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -18199,6 +16889,7 @@ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6.0" }, @@ -18211,22 +16902,13 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "dev": true - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } + "license": "Apache-2.0" }, "node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -18238,14 +16920,15 @@ "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", "dev": true, + "license": "MIT", "dependencies": { "es5-ext": "~0.10.2" } }, "node_modules/lru.min": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.3.tgz", - "integrity": "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", "dev": true, "license": "MIT", "engines": { @@ -18258,6 +16941,28 @@ "url": "https://github.com/sponsors/wellwelwel" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -18274,24 +16979,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/make-fetch-happen": { "version": "15.0.2", @@ -18316,21 +17009,12 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/make-fetch-happen/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -18352,13 +17036,15 @@ "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", "dev": true, + "license": "MIT", "dependencies": { "object-visit": "^1.0.0" }, @@ -18371,6 +17057,7 @@ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", "dev": true, + "license": "MIT", "dependencies": { "repeat-string": "^1.0.0" }, @@ -18392,15 +17079,16 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/memoizee": { @@ -18408,6 +17096,7 @@ "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", "dev": true, + "license": "ISC", "dependencies": { "d": "^1.0.2", "es5-ext": "^0.10.64", @@ -18430,13 +17119,13 @@ "license": "MIT" }, "node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16.10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -18446,6 +17135,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -18453,26 +17143,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, "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 + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -18482,6 +17165,7 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -18491,6 +17175,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -18499,36 +17184,42 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8.6" + "bin": { + "mime": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=10.0.0" } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-fn": { @@ -18536,6 +17227,7 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -18564,10 +17256,11 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -18579,7 +17272,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -18609,6 +17302,16 @@ "node": ">=0.10.0" } }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/minimist-options/node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -18620,10 +17323,11 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -18659,24 +17363,12 @@ "encoding": "^0.1.13" } }, - "node_modules/minipass-fetch/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "minipass": "^3.0.0" }, @@ -18689,6 +17381,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -18696,11 +17389,19 @@ "node": ">=8" } }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -18713,6 +17414,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -18720,11 +17422,19 @@ "node": ">=8" } }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -18737,6 +17447,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -18744,29 +17455,24 @@ "node": ">=8" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } + "license": "ISC" }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">=8" + "node": ">= 18" } }, "node_modules/mixin-deep": { @@ -18774,6 +17480,7 @@ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, + "license": "MIT", "dependencies": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -18786,7 +17493,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -18794,285 +17501,299 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mocha": { - "version": "11.7.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", - "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", "dev": true, "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/mocha/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, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/mongodb": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", + "integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^7.0.0", + "mongodb-connection-string-url": "^7.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", + "snappy": "^7.3.2", + "socks": "^2.8.6" }, "peerDependenciesMeta": { - "supports-color": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { "optional": true } } }, - "node_modules/mocha/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "node_modules/mongodb-connection-string-url": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz", + "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, "engines": { - "node": ">=0.3.1" + "node": ">=20.19.0" } }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/mongoose": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.1.6.tgz", + "integrity": "sha512-ZrtgRsJKtW3od36TVXtAnrNHOO3rqhsqfVut6IzyWyJeLLeLTqW66qej/0qB37GZd5jL06nHfAceTikkfnSqbA==", "dev": true, "license": "MIT", + "dependencies": { + "kareem": "3.0.0", + "mongodb": "~7.0", + "mpath": "0.9.0", + "mquery": "6.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, "engines": { - "node": ">=10" + "node": ">=20.19.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/mongoose" } }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0.0" } }, - "node_modules/mocha/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==", + "node_modules/mqtt": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.0.tgz", + "integrity": "sha512-KC+wAssYk83Qu5bT8YDzDYgUJxPhbLeVsDvpY2QvL28PnXYJzC2WkKruyMUgBAZaQ7h9lo9k2g4neRNUUxzgMw==", "dev": true, - "license": "ISC", + "license": "MIT", "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" + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" }, "bin": { - "glob": "dist/esm/bin.mjs" + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/mqtt/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/mocha/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/mqtt/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/mqtt/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "safe-buffer": "~5.2.0" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/mquery": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", + "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=20.19.0" } }, - "node_modules/mocha/node_modules/ms": { + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 10.16.0" } }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "mime-db": "1.52.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/multimatch": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", "dev": true, "license": "MIT", + "dependencies": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, "engines": { "node": ">=10" }, @@ -19080,1154 +17801,1011 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "node_modules/multimatch/node_modules/array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/mongoose": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.2.1.tgz", - "integrity": "sha512-fmNLwgct5km7iL1MqvTMncarR1E1TIw2lmc9A4UoDVdS7AQe95K+DnRK0qATkSUdwUC9V/5wlDcqnkQQjbSRkA==", + "node_modules/multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha512-7ZxrUybYv9NonoXgwoOqtStIu18D1c3eFZj27hqgf5kBrBF8Q+tE8V0MW8dKM5QLkQPh1JhhbKgHLY9kifov4Q==", "dev": true, "license": "MIT", "dependencies": { - "kareem": "3.2.0", - "mongodb": "~7.0", - "mpath": "0.9.0", - "mquery": "6.0.0", - "ms": "2.1.3", - "sift": "17.1.3" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" + "duplexer2": "0.0.2" } }, - "node_modules/mongoose/node_modules/@types/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "node_modules/mute-stdout": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-2.0.0.tgz", + "integrity": "sha512-32GSKM3Wyc8dg/p39lWPKYu8zci9mJFzV1Np9Of0ZEpe6Fhssn/FbI7ywAMd40uX+p3ZKh3T5EeCFv81qS3HmQ==", "dev": true, "license": "MIT", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/mongoose/node_modules/bson": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz", - "integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==", - "dev": true, - "license": "Apache-2.0", "engines": { - "node": ">=20.19.0" + "node": ">= 10.13.0" } }, - "node_modules/mongoose/node_modules/mongodb": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", - "integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==", + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/saslprep": "^1.3.0", - "bson": "^7.0.0", - "mongodb-connection-string-url": "^7.0.0" - }, + "license": "ISC", "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.806.0", - "@mongodb-js/zstd": "^7.0.0", - "gcp-metadata": "^7.0.1", - "kerberos": "^7.0.0", - "mongodb-client-encryption": ">=7.0.0 <7.1.0", - "snappy": "^7.3.2", - "socks": "^2.8.6" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/mongoose/node_modules/mongodb-connection-string-url": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz", - "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==", + "node_modules/mysql2": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.17.1.tgz", + "integrity": "sha512-UzIzdVwPXPoZm+FaJ4lNsRt28HtUwt68gpLH7NP1oSjd91M5Qn1XJzbIsSRMRc5CV3pvktLNshmbaFfMYqPBhQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@types/whatwg-url": "^13.0.0", - "whatwg-url": "^14.1.0" + "aws-ssl-profiles": "^1.1.2", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.2", + "long": "^5.3.2", + "lru.min": "^1.1.3", + "named-placeholders": "^1.1.6", + "seq-queue": "^0.0.5", + "sql-escaper": "^1.3.2" }, "engines": { - "node": ">=20.19.0" + "node": ">= 8.0" } }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/mysql2/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "dev": true, - "license": "MIT" + "license": "Apache-2.0" }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "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, - "engines": { - "node": ">=4.0.0" + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, - "node_modules/mqtt": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.0.tgz", - "integrity": "sha512-KC+wAssYk83Qu5bT8YDzDYgUJxPhbLeVsDvpY2QvL28PnXYJzC2WkKruyMUgBAZaQ7h9lo9k2g4neRNUUxzgMw==", + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", "dev": true, "license": "MIT", "dependencies": { - "@types/readable-stream": "^4.0.21", - "@types/ws": "^8.18.1", - "commist": "^3.2.0", - "concat-stream": "^2.0.0", - "debug": "^4.4.1", - "help-me": "^5.0.0", - "lru-cache": "^10.4.3", - "minimist": "^1.2.8", - "mqtt-packet": "^9.0.2", - "number-allocator": "^1.0.14", - "readable-stream": "^4.7.0", - "rfdc": "^1.4.1", - "socks": "^2.8.6", - "split2": "^4.2.0", - "worker-timers": "^8.0.23", - "ws": "^8.18.3" - }, - "bin": { - "mqtt": "build/bin/mqtt.js", - "mqtt_pub": "build/bin/pub.js", - "mqtt_sub": "build/bin/sub.js" + "lru.min": "^1.1.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=8.0.0" } }, - "node_modules/mqtt-packet": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", - "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "node_modules/nan": { + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz", + "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==", "dev": true, "license": "MIT", - "dependencies": { - "bl": "^6.0.8", - "debug": "^4.3.4", - "process-nextick-args": "^2.0.1" - } + "optional": true }, - "node_modules/mqtt-packet/node_modules/bl": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.16.tgz", - "integrity": "sha512-V/kz+z2Mx5/6qDfRCilmrukUXcXuCoXKg3/3hDvzKKoSUx8CJKudfIoT29XZc3UE9xBvxs5qictiHdprwtteEg==", + "node_modules/nano-spawn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.1.0.tgz", + "integrity": "sha512-yTW+2okrElHiH4fsiz/+/zc0EDo9BDDoC3iKk8dpv1GeRc9nUWzUZHx6TofMWErchhUQR8hY9/Eu1Uja9x1nqA==", "dev": true, - "dependencies": { - "@types/readable-stream": "^4.0.0", - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^4.2.0" + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" } }, - "node_modules/mqtt-packet/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "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" + "url": "https://github.com/sponsors/ai" } ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/mqtt-packet/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/mqtt-packet/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/nanomatch/node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/mqtt-packet/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, + "license": "MIT", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/mqtt-packet/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/nanomatch/node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/mqtt/node_modules/buffer": { + "node_modules/nanomatch/node_modules/kind-of": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "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" - } - ], "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/mqtt/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/mqtt/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/mqtt/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/mqtt/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "node_modules/node-gyp": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.2.0.tgz", + "integrity": "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==", "dev": true, "license": "MIT", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^15.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/mqtt/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/mquery": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", - "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", + "node_modules/node-gyp/node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=20.19.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/multer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", - "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "dev": true, - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">= 10.16.0" + "node": ">=20" } }, - "node_modules/multimatch": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", - "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", + "node_modules/node-gyp/node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@types/minimatch": "^3.0.3", - "array-differ": "^3.0.0", - "array-union": "^2.1.0", - "arrify": "^2.0.1", - "minimatch": "^3.0.4" + "abbrev": "^4.0.0" }, - "engines": { - "node": ">=10" + "bin": { + "nopt": "bin/nopt.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/multimatch/node_modules/array-differ": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", - "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "node_modules/node-gyp/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=8" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/multipipe": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha512-7ZxrUybYv9NonoXgwoOqtStIu18D1c3eFZj27hqgf5kBrBF8Q+tE8V0MW8dKM5QLkQPh1JhhbKgHLY9kifov4Q==", + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "dev": true, + "license": "ISC", "dependencies": { - "duplexer2": "0.0.2" + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/mute-stdout": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-2.0.0.tgz", - "integrity": "sha512-32GSKM3Wyc8dg/p39lWPKYu8zci9mJFzV1Np9Of0ZEpe6Fhssn/FbI7ywAMd40uX+p3ZKh3T5EeCFv81qS3HmQ==", + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, "engines": { - "node": ">= 10.13.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "node_modules/normalize-package-data": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-7.0.1.tgz", + "integrity": "sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, "engines": { "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/mysql2": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.17.1.tgz", - "integrity": "sha512-UzIzdVwPXPoZm+FaJ4lNsRt28HtUwt68gpLH7NP1oSjd91M5Qn1XJzbIsSRMRc5CV3pvktLNshmbaFfMYqPBhQ==", + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "aws-ssl-profiles": "^1.1.2", - "denque": "^2.1.0", - "generate-function": "^2.3.1", - "iconv-lite": "^0.7.2", - "long": "^5.3.2", - "lru.min": "^1.1.3", - "named-placeholders": "^1.1.6", - "seq-queue": "^0.0.5", - "sql-escaper": "^1.3.2" + "lru-cache": "^10.0.1" }, "engines": { - "node": ">= 8.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/mysql2/node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "Apache-2.0" + "license": "ISC" }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "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, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/named-placeholders": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", - "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "node_modules/now-and-later": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz", + "integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==", "dev": true, "license": "MIT", "dependencies": { - "lru.min": "^1.1.0" + "once": "^1.4.0" }, "engines": { - "node": ">=8.0.0" + "node": ">= 10.13.0" } }, - "node_modules/nan": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", - "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", - "dev": true, - "optional": true - }, - "node_modules/nano-spawn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", - "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "node_modules/npm-bundled": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", + "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.17" + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "node_modules/npm-install-checks": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.2.tgz", + "integrity": "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "semver": "^7.1.1" }, "engines": { - "node": ">=0.10.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nanomatch/node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", "dev": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nanomatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/npm-package-arg": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.1.tgz", + "integrity": "sha512-6zqls5xFvJbgFjB1B2U6yITtyGBjDBORB7suI4zA4T/sZ1OmkMFlaQSNB/4K0LtXNA1t4OprAFxPisadK5O2ag==", "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/nats": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", - "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "node_modules/npm-packlist": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", + "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "nkeys.js": "1.1.0" + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" }, "engines": { - "node": ">= 14.0.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 0.6" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/nested-error-stacks": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", - "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", - "dev": true - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - }, - "node_modules/nkeys.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", - "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", "dev": true, + "license": "ISC", "dependencies": { - "tweetnacl": "1.0.3" + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" }, "engines": { - "node": ">=10.0.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/nkeys.js/node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true - }, - "node_modules/node-gyp": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.2.0.tgz", - "integrity": "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==", + "node_modules/npm-pick-manifest/node_modules/npm-install-checks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^15.0.0", - "nopt": "^9.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.5", - "tar": "^7.5.4", - "tinyglobby": "^0.2.12", - "which": "^6.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "semver": "^7.1.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/node-gyp/node_modules/abbrev": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", - "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", "dev": true, "license": "ISC", "engines": { "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/node-gyp/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/node-gyp/node_modules/isexe": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", - "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "node_modules/npm-registry-fetch": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.0.tgz", + "integrity": "sha512-xyZLfs7TxPu/WKjHUs0jZOPinzBAI32kEUel6za0vH+JUTnFZ5zbHI1ZoGZRDm6oMjADtrli6FxtMlk/5ABPNw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^15.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^5.0.0" + }, "engines": { - "node": ">=20" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/node-gyp/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "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, "license": "MIT", "dependencies": { - "minipass": "^7.1.2" + "path-key": "^3.0.0" }, "engines": { - "node": ">= 18" + "node": ">=8" } }, - "node_modules/node-gyp/node_modules/nopt": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", - "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "abbrev": "^4.0.0" + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/nx": { + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/nx/-/nx-22.6.3.tgz", + "integrity": "sha512-8eIkEAlvkTvR2zY+yjhuTxMD6z4AtM1SumSBbwMmUMEXMtXE88fH0RL59T5V6MLjaov1exUM3lhUqPE3IyuBPg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@ltd/j-toml": "^1.38.0", + "@napi-rs/wasm-runtime": "0.2.4", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.2", + "@zkochan/js-yaml": "0.0.7", + "axios": "^1.12.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "front-matter": "^4.0.2", + "ignore": "^7.0.5", + "jest-diff": "^30.0.2", + "jsonc-parser": "3.2.0", + "lines-and-columns": "2.0.3", + "minimatch": "10.2.4", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "picocolors": "^1.1.0", + "resolve.exports": "2.0.3", + "semver": "^7.6.3", + "string-width": "^4.2.3", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tree-kill": "^1.2.2", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yaml": "^2.6.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" }, "bin": { - "nopt": "bin/nopt.js" + "nx": "bin/nx.js", + "nx-cloud": "bin/nx-cloud.js" }, - "engines": { - "node": "^20.17.0 || >=22.9.0" + "optionalDependencies": { + "@nx/nx-darwin-arm64": "22.6.3", + "@nx/nx-darwin-x64": "22.6.3", + "@nx/nx-freebsd-x64": "22.6.3", + "@nx/nx-linux-arm-gnueabihf": "22.6.3", + "@nx/nx-linux-arm64-gnu": "22.6.3", + "@nx/nx-linux-arm64-musl": "22.6.3", + "@nx/nx-linux-x64-gnu": "22.6.3", + "@nx/nx-linux-x64-musl": "22.6.3", + "@nx/nx-win32-arm64-msvc": "22.6.3", + "@nx/nx-win32-x64-msvc": "22.6.3" + }, + "peerDependencies": { + "@swc-node/register": "^1.11.1", + "@swc/core": "^1.15.8" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "node_modules/nx/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "18 || 20 || >=22" } }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "node_modules/nx/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=10" + "node": "18 || 20 || >=22" } }, - "node_modules/node-gyp/node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "node_modules/nx/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/node-gyp/node_modules/which": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", - "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "node_modules/nx/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^4.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">= 4" } }, - "node_modules/node-gyp/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "node_modules/nx/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/node-machine-id": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", - "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "node_modules/nx/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=8" } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "license": "Apache-2.0", + "engines": { + "node": "*" } }, - "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, + "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==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/now-and-later": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz", - "integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==", + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", "dev": true, + "license": "MIT", "dependencies": { - "once": "^1.4.0" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "engines": { - "node": ">= 10.13.0" + "node": ">=0.10.0" } }, - "node_modules/npm-bundled": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", - "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^4.0.0" - }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">= 6" } }, - "node_modules/npm-install-checks": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.2.tgz", - "integrity": "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm-install-checks/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "node_modules/npm-normalize-package-bin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", - "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.0" + }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=0.10.0" } }, - "node_modules/npm-package-arg": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.1.tgz", - "integrity": "sha512-6zqls5xFvJbgFjB1B2U6yITtyGBjDBORB7suI4zA4T/sZ1OmkMFlaQSNB/4K0LtXNA1t4OprAFxPisadK5O2ag==", + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "hosted-git-info": "^9.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm-package-arg/node_modules/hosted-git-info": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", - "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "lru-cache": "^11.1.0" + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=0.10.0" } }, - "node_modules/npm-package-arg/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/npm-packlist": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", - "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", + "node_modules/object.omit/node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "ignore-walk": "^8.0.0", - "proc-log": "^6.0.0" + "for-in": "^1.0.1" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=0.10.0" } }, - "node_modules/npm-packlist/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "node_modules/object.omit/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=0.10.0" } }, - "node_modules/npm-pick-manifest": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", - "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "npm-install-checks": "^8.0.0", - "npm-normalize-package-bin": "^5.0.0", - "npm-package-arg": "^13.0.0", - "semver": "^7.3.5" + "isobject": "^3.0.1" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=0.10.0" } }, - "node_modules/npm-pick-manifest/node_modules/npm-install-checks": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", - "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=14.0.0" } }, - "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", - "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", - "dev": true, - "license": "ISC", + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">= 0.8" } }, - "node_modules/npm-pick-manifest/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "dependencies": { + "wrappy": "1" } }, - "node_modules/npm-registry-fetch": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.0.tgz", - "integrity": "sha512-xyZLfs7TxPu/WKjHUs0jZOPinzBAI32kEUel6za0vH+JUTnFZ5zbHI1ZoGZRDm6oMjADtrli6FxtMlk/5ABPNw==", + "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, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/redact": "^3.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^15.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minizlib": "^3.0.1", - "npm-package-arg": "^13.0.0", - "proc-log": "^5.0.0" + "mimic-fn": "^2.1.0" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-registry-fetch/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dev": true, "license": "MIT", "dependencies": { - "minipass": "^7.1.2" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">= 18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.0.0" + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/number-allocator": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", - "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "node_modules/ora/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.3.1", - "js-sdsl": "4.3.0" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/number-allocator/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/number-allocator/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nx": { - "version": "22.3.3", - "resolved": "https://registry.npmjs.org/nx/-/nx-22.3.3.tgz", - "integrity": "sha512-pOxtKWUfvf0oD8Geqs8D89Q2xpstRTaSY+F6Ut/Wd0GnEjUjO32SS1ymAM6WggGPHDZN4qpNrd5cfIxQmAbRLg==", + "node_modules/ora/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@napi-rs/wasm-runtime": "0.2.4", - "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "3.0.2", - "@zkochan/js-yaml": "0.0.7", - "axios": "^1.12.0", - "chalk": "^4.1.0", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^8.0.1", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "enquirer": "~2.3.6", - "figures": "3.2.0", - "flat": "^5.0.2", - "front-matter": "^4.0.2", - "ignore": "^7.0.5", - "jest-diff": "^30.0.2", - "jsonc-parser": "3.2.0", - "lines-and-columns": "2.0.3", - "minimatch": "9.0.3", - "node-machine-id": "1.1.12", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "ora": "5.3.0", - "resolve.exports": "2.0.3", - "semver": "^7.6.3", - "string-width": "^4.2.3", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tree-kill": "^1.2.2", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0", - "yaml": "^2.6.0", - "yargs": "^17.6.2", - "yargs-parser": "21.1.1" - }, - "bin": { - "nx": "bin/nx.js", - "nx-cloud": "bin/nx-cloud.js" - }, - "optionalDependencies": { - "@nx/nx-darwin-arm64": "22.3.3", - "@nx/nx-darwin-x64": "22.3.3", - "@nx/nx-freebsd-x64": "22.3.3", - "@nx/nx-linux-arm-gnueabihf": "22.3.3", - "@nx/nx-linux-arm64-gnu": "22.3.3", - "@nx/nx-linux-arm64-musl": "22.3.3", - "@nx/nx-linux-x64-gnu": "22.3.3", - "@nx/nx-linux-x64-musl": "22.3.3", - "@nx/nx-win32-arm64-msvc": "22.3.3", - "@nx/nx-win32-x64-msvc": "22.3.3" - }, - "peerDependencies": { - "@swc-node/register": "^1.8.0", - "@swc/core": "^1.3.85" - }, - "peerDependenciesMeta": { - "@swc-node/register": { - "optional": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "@swc/core": { - "optional": true + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - } - }, - "node_modules/nx/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, + ], "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/nx/node_modules/cli-cursor": { + "node_modules/ora/node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", @@ -20240,71 +18818,108 @@ "node": ">=8" } }, - "node_modules/nx/node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "node_modules/ora/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 6" } }, - "node_modules/nx/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, "engines": { "node": ">=8" } }, - "node_modules/nx/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "node_modules/ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.1" } }, - "node_modules/nx/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/oxlint": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.58.0.tgz", + "integrity": "sha512-t4s9leczDMqlvOSjnbCQe7gtoLkWgBGZ7sBdCJ9EOj5IXFSG/X7OAzK4yuH4iW+4cAYe8kLFbC8tuYMwWZm+Cg==", "dev": true, "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, "engines": { - "node": ">= 4" + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.58.0", + "@oxlint/binding-android-arm64": "1.58.0", + "@oxlint/binding-darwin-arm64": "1.58.0", + "@oxlint/binding-darwin-x64": "1.58.0", + "@oxlint/binding-freebsd-x64": "1.58.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.58.0", + "@oxlint/binding-linux-arm-musleabihf": "1.58.0", + "@oxlint/binding-linux-arm64-gnu": "1.58.0", + "@oxlint/binding-linux-arm64-musl": "1.58.0", + "@oxlint/binding-linux-ppc64-gnu": "1.58.0", + "@oxlint/binding-linux-riscv64-gnu": "1.58.0", + "@oxlint/binding-linux-riscv64-musl": "1.58.0", + "@oxlint/binding-linux-s390x-gnu": "1.58.0", + "@oxlint/binding-linux-x64-gnu": "1.58.0", + "@oxlint/binding-linux-x64-musl": "1.58.0", + "@oxlint/binding-openharmony-arm64": "1.58.0", + "@oxlint/binding-win32-arm64-msvc": "1.58.0", + "@oxlint/binding-win32-ia32-msvc": "1.58.0", + "@oxlint/binding-win32-x64-msvc": "1.58.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=0.18.0" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + } } }, - "node_modules/nx/node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=4" } }, - "node_modules/nx/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "aggregate-error": "^3.0.0" }, "engines": { "node": ">=10" @@ -20313,817 +18928,670 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nx/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/p-map-series": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz", + "integrity": "sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/nx/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "node_modules/p-pipe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", + "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", "dev": true, "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nx/node_modules/ora": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", - "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nx/node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", "dev": true, "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, "engines": { "node": ">=8" } }, - "node_modules/nx/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "dev": true, - "dependencies": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/nyc/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/p-waterfall": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz", + "integrity": "sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "p-reduce": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nyc/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } + "license": "BlueOak-1.0.0" }, - "node_modules/nyc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/pacote": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.1.tgz", + "integrity": "sha512-LHGIUQUrcDIJUej53KJz1BPvUuHrItrR2yrnN0Kl9657cJ0ZT6QJHk9wWPBnQZhYT5KLyZWrk9jaYc2aKDu4yw==", "dev": true, + "license": "ISC", "dependencies": { - "color-name": "1.1.3" + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^4.0.0", + "ssri": "^12.0.0", + "tar": "^7.4.3" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/nyc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/nyc/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/nyc/node_modules/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", + "node_modules/pacote/node_modules/@npmcli/git": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.3.tgz", + "integrity": "sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==", "dev": true, + "license": "ISC", "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/nyc/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/pacote/node_modules/@npmcli/promise-spawn": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.3.tgz", + "integrity": "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==", "dev": true, + "license": "ISC", "dependencies": { - "locate-path": "^3.0.0" + "which": "^5.0.0" }, "engines": { - "node": ">=6" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha512-3TOY+4TKV0Ml83PXJQY+JFQaHNV38lzQDIzzXYg1kWdBLenGgoZhAs0CKgzI31vi2pWEpQMq/Yi4bpKwCPkw7g==", + "node_modules/pacote/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", "dev": true, + "license": "ISC", "dependencies": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - } - }, - "node_modules/nyc/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, + "lru-cache": "^10.0.1" + }, "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "node_modules/pacote/node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", "dev": true, + "license": "ISC", "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "node_modules/pacote/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/nyc/node_modules/istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "node_modules/pacote/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, - "dependencies": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6" - } + "license": "ISC" }, - "node_modules/nyc/node_modules/istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "node_modules/pacote/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", + "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", "dev": true, + "license": "ISC", "dependencies": { - "html-escaper": "^2.0.0" + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" }, "engines": { - "node": ">=6" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/pacote/node_modules/npm-pick-manifest/node_modules/npm-package-arg": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", + "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", "dev": true, + "license": "ISC", "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/pacote/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, + "license": "ISC", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">=6" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "callsites": "^3.0.0" }, "engines": { "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nyc/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/parse-conflict-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-4.0.0.tgz", + "integrity": "sha512-37CN2VtcuvKgHUs8+0b1uJeEsbGn61GRHz469C94P5xiOoqpDYJYwjg4RY9Vmz39WyZAVkR5++nbJwLMIgOCnQ==", "dev": true, + "license": "ISC", "dependencies": { - "p-limit": "^2.0.0" + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" }, "engines": { - "node": ">=6" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/parse-conflict-json/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/nyc/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, "engines": { - "node": ">=6" + "node": ">=0.8" } }, - "node_modules/nyc/node_modules/read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "node_modules/parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", "dev": true, + "license": "MIT", "dependencies": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/nyc/node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/parse-glob/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/nyc/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/parse-glob/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "is-extglob": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/nyc/node_modules/strip-ansi": { + "node_modules/parse-json": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^4.1.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nyc/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/nyc/node_modules/test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "node_modules/parse-json/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, - "dependencies": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/nyc/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, - "bin": { - "uuid": "bin/uuid" + "license": "MIT", + "engines": { + "node": ">= 0.10" } }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/nyc/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, - "node_modules/nyc/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "node_modules/parse-path": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz", + "integrity": "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "protocols": "^2.0.0" } }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "node_modules/parse-url": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", + "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "parse-path": "^7.0.0" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "dev": true, - "engines": { - "node": "*" - } - }, - "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==", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, + "license": "MIT", "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" + "parse5": "^6.0.1" } }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true, + "license": "MIT" }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/object-visit": { + "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "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, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, "license": "MIT", "dependencies": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "path-root-regex": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true, - "dependencies": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/object.omit/node_modules/for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "for-in": "^1.0.1" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.omit/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" + "node": "18 || 20 || >=22" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "node_modules/path-starts-with": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-starts-with/-/path-starts-with-2.0.1.tgz", + "integrity": "sha512-wZ3AeiRBRlNwkdUxvBANh0+esnt38DLffHDujZyRHkqkaKHTglnY2EP5UX3b8rdeiSutgO4y9NEJwXezNP5vHg==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, + "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "pify": "^3.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/optionator/node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/ordered-read-streams": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, - "dependencies": { - "readable-stream": "^2.0.1" - } - }, - "node_modules/ordered-read-streams/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "license": "MIT" }, - "node_modules/ordered-read-streams/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", "dev": true, + "license": [ + "MIT", + "Apache2" + ], "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "through": "~2.3" } }, - "node_modules/ordered-read-streams/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" }, - "node_modules/ordered-read-streams/node_modules/string_decoder": { + "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } + "license": "ISC" }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, "engines": { - "node": ">=4" + "node": ">=0.10" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", "dev": true, "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, "engines": { "node": ">=10" }, @@ -21131,1221 +19599,1476 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz", - "integrity": "sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==", + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/p-pipe": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", - "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "pinkie": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "dev": true, + "node_modules/pino": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", "license": "MIT", "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "pino": "bin.js" } }, - "node_modules/p-queue/node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true, + "node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT" }, - "node_modules/p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { - "p-finally": "^1.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/p-waterfall": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz", - "integrity": "sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { - "p-reduce": "^2.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/package-json-from-dist": { + "node_modules/plugin-error": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true - }, - "node_modules/pacote": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.1.tgz", - "integrity": "sha512-LHGIUQUrcDIJUej53KJz1BPvUuHrItrR2yrnN0Kl9657cJ0ZT6QJHk9wWPBnQZhYT5KLyZWrk9jaYc2aKDu4yw==", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/git": "^6.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^10.0.0", - "cacache": "^20.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^13.0.0", - "npm-packlist": "^10.0.1", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^19.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^4.0.0", - "ssri": "^12.0.0", - "tar": "^7.4.3" - }, - "bin": { - "pacote": "bin/index.js" + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">= 0.10" } }, - "node_modules/pacote/node_modules/@npmcli/git": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.3.tgz", - "integrity": "sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==", + "node_modules/plugin-error/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/promise-spawn": "^8.0.0", - "ini": "^5.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^10.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^5.0.0" + "ansi-wrap": "^0.1.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=0.10.0" } }, - "node_modules/pacote/node_modules/@npmcli/promise-spawn": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.3.tgz", - "integrity": "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==", + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", "dev": true, - "license": "ISC", - "dependencies": { - "which": "^5.0.0" - }, + "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=0.10.0" } }, - "node_modules/pacote/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.4" } }, - "node_modules/pacote/node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "lru-cache": "^10.0.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/pacote/node_modules/ini": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", - "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/pacote/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, "engines": { - "node": ">=16" + "node": ">=4" } }, - "node_modules/pacote/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==", + "node_modules/postcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/pacote/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "node_modules/preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==", "dev": true, "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, "engines": { - "node": ">= 18" + "node": ">=0.10.0" } }, - "node_modules/pacote/node_modules/npm-pick-manifest": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", - "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^7.1.0", - "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^12.0.0", - "semver": "^7.3.5" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/pacote/node_modules/npm-pick-manifest/node_modules/npm-package-arg": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", - "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", + "node_modules/pretty-format": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "hosted-git-info": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/pacote/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { "node": ">=10" - } - }, - "node_modules/pacote/node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" }, - "engines": { - "node": ">=18" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pacote/node_modules/which": { + "node_modules/proc-log": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", "dev": true, "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, "engines": { "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/pacote/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.6.0" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/parse-conflict-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-4.0.0.tgz", - "integrity": "sha512-37CN2VtcuvKgHUs8+0b1uJeEsbGn61GRHz469C94P5xiOoqpDYJYwjg4RY9Vmz39WyZAVkR5++nbJwLMIgOCnQ==", + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/proggy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proggy/-/proggy-3.0.0.tgz", + "integrity": "sha512-QE8RApCM3IaRRxVzxrjbgNMpQEX6Wu0p0KBeoSiSEw5/bsGwZHsshF4LCxH2jp/r6BU+bqA3LrMDEYNfJnpD8Q==", "dev": true, "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^4.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" - }, "engines": { "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/parse-conflict-json/node_modules/json-parse-even-better-errors": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", - "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=0.4.0" } }, - "node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "dev": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-breaker": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/promise-breaker/-/promise-breaker-6.0.0.tgz", + "integrity": "sha512-BthzO9yTPswGf7etOBiHCVuugs2N01/Q/94dIPls48z2zCmrnDptUUZzfIb+41xq0MnYZ/BzmOd6ikDR4ibNZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/promise-call-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.2.tgz", + "integrity": "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==", + "dev": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, "license": "MIT", "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "err-code": "^2.0.2", + "retry": "^0.12.0" }, "engines": { - "node": ">=0.8" + "node": ">=10" } }, - "node_modules/parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, - "dependencies": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/parse-glob/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "node_modules/promzard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/promzard/-/promzard-2.0.0.tgz", + "integrity": "sha512-Ncd0vyS2eXGOjchIRg6PVCYKetJYrW1BSbbIo+bKdig61TB6nH2RQNF2uP+qMpsI73L/jURLWojcw8JNIKZ3gg==", "dev": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/parse-glob/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", "dependencies": { - "is-extglob": "^1.0.0" + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/protobufjs/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/protocols": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz", + "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==", "dev": true, + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.10" } }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/parse-path": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz", - "integrity": "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==", + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", "dev": true, "license": "MIT", "dependencies": { - "protocols": "^2.0.0" + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" } }, - "node_modules/parse-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", - "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", + "node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "license": "MIT", "dependencies": { - "parse-path": "^7.0.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" } }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "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, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true, "license": "MIT" }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true + "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" + } + ], + "license": "MIT" }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/path-key": { + "node_modules/randomatic": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", "dev": true, "license": "MIT", + "dependencies": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 0.10.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "node_modules/randomatic/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true, "license": "MIT", - "dependencies": { - "path-root-regex": "^0.1.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "node_modules/randomatic/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "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, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.10" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" }, - "node_modules/path-starts-with": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-starts-with/-/path-starts-with-2.0.1.tgz", - "integrity": "sha512-wZ3AeiRBRlNwkdUxvBANh0+esnt38DLffHDujZyRHkqkaKHTglnY2EP5UX3b8rdeiSutgO4y9NEJwXezNP5vHg==", + "node_modules/read": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/read/-/read-4.1.0.tgz", + "integrity": "sha512-uRfX6K+f+R8OOrYScaM3ixPY4erg69f8DN6pgTvMcA9iRc8iDhwrA4m3Yu8YYKsXJgVvum+m8PkRboZwwuLzYA==", "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, "engines": { - "node": ">=8" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node_modules/read-cmd-shim": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", + "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/path-type": { + "node_modules/read-pkg": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "license": "MIT", "dependencies": { - "pify": "^3.0.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { "node": ">=4" } }, - "node_modules/path-type/node_modules/pify": { + "node_modules/read-pkg-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", "dev": true, "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, "engines": { "node": ">=4" } }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, "engines": { - "node": "*" + "node": ">=4" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, + "license": "MIT", "dependencies": { - "through": "~2.3" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "p-try": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=4" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" }, "engines": { - "node": ">=0.10" + "node": ">=4" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, + "license": "MIT", "dependencies": { - "pinkie": "^2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/pino": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", - "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "@pinojs/redact": "^0.4.0", - "atomic-sleep": "^1.0.0", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^5.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^3.0.0" - }, - "bin": { - "pino": "bin.js" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/pino-abstract-transport": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", - "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "node_modules/read-pkg/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "license": "MIT", "dependencies": { - "split2": "^4.0.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" } }, - "node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/pino/node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" + "license": "ISC", + "bin": { + "semver": "bin/semver" + } }, - "node_modules/pkg-dir": { + "node_modules/read-pkg/node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^3.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 12.13.0" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">=6" + "node": ">= 10.13.0" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/redis": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.10.0.tgz", + "integrity": "sha512-0/Y+7IEiTgVGPrLFKy8oAEArSyEJkU0zvgV5xyi9NzNQ+SLZmyFbUsWIbgPcd4UdUh00opXGKlXJwMmsis5Byw==", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^2.0.0" + "@redis/bloom": "5.10.0", + "@redis/client": "5.10.0", + "@redis/json": "5.10.0", + "@redis/search": "5.10.0", + "@redis/time-series": "5.10.0" }, "engines": { - "node": ">=6" + "node": ">= 18" } }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" + "redis-errors": "^1.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=4" } }, - "node_modules/plugin-error/node_modules/ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-wrap": "^0.1.0" + "is-equal-shallow": "^0.1.3" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", "dev": true, "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" }, "engines": { - "node": ">=4" + "node": ">= 0.10" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true, - "engines": { - "node": ">= 0.8.0" - } + "license": "ISC" }, - "node_modules/preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==", + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node": ">=0.10" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", - "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "node_modules/replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/replace-homedir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-2.0.0.tgz", + "integrity": "sha512-bgEuQQ/BHW0XkkJtawzrfzHFSN70f/3cNOiHa2QsYxqrjaC30X1k74FJ6xswVBP0sr0SpGIdVFuPwfrYziVeyw==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "fast-diff": "^1.1.2" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" }, "engines": { - "node": ">=6.0.0" + "node": ">= 6" } }, - "node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.12" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/request/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "node_modules/request/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">= 0.6" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/request/node_modules/qs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", + "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.6.0" + "node": ">=0.6" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/process-warning": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", - "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==", - "dev": true - }, - "node_modules/proggy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proggy/-/proggy-3.0.0.tgz", - "integrity": "sha512-QE8RApCM3IaRRxVzxrjbgNMpQEX6Wu0p0KBeoSiSEw5/bsGwZHsshF4LCxH2jp/r6BU+bqA3LrMDEYNfJnpD8Q==", + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" + "license": "MIT", + "bin": { + "uuid": "bin/uuid" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">=0.10.0" } }, - "node_modules/promise-all-reject-late": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", - "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", - "dev": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/promise-breaker": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/promise-breaker/-/promise-breaker-6.0.0.tgz", - "integrity": "sha512-BthzO9yTPswGf7etOBiHCVuugs2N01/Q/94dIPls48z2zCmrnDptUUZzfIb+41xq0MnYZ/BzmOd6ikDR4ibNZA==", - "dev": true + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" }, - "node_modules/promise-call-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.2.tgz", - "integrity": "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==", + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "license": "MIT", "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/promise-retry/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, "engines": { - "node": ">= 4" + "node": ">=0.10.0" } }, - "node_modules/promzard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/promzard/-/promzard-2.0.0.tgz", - "integrity": "sha512-Ncd0vyS2eXGOjchIRg6PVCYKetJYrW1BSbbIo+bKdig61TB6nH2RQNF2uP+qMpsI73L/jURLWojcw8JNIKZ3gg==", + "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, - "license": "ISC", - "dependencies": { - "read": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=8" } }, - "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "node_modules/resolve-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", + "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", "dev": true, - "hasInstallScript": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" + "value-or-function": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 10.13.0" } }, - "node_modules/protobufjs/node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "dev": true + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } }, - "node_modules/protocols": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz", - "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==", + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", "dev": true, "license": "MIT" }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">= 0.10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, - "license": "MIT" - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/psl": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.13.0.tgz", - "integrity": "sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw==", + "node_modules/restore-cursor/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, - "dependencies": { - "punycode": "^2.3.1" + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "license": "BSD-3-Clause", + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", "dependencies": { - "side-channel": "^1.1.0" + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=0.6" + "node": "^20.19.0 || >=22.12.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" } }, - "node_modules/querystringify": { + "node_modules/router": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } }, - "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==", + "node_modules/router/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/run-async": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", + "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -22360,4431 +21083,4238 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "dev": true, - "license": "MIT" - }, - "node_modules/randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dev": true, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", "dependencies": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "engines": { - "node": ">= 0.10.0" + "tslib": "^2.1.0" } }, - "node_modules/randomatic/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "node_modules/rxjs-compat": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.6.7.tgz", + "integrity": "sha512-szN4fK+TqBPOFBcBcsR0g2cmTTUF/vaFEOZNuSdfU8/pGFnNmmn2u8SystYXG1QMrjOPBc6XTKHMVfENDf6hHw==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "Apache-2.0" }, - "node_modules/randomatic/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "^5.1.0" + "ret": "~0.1.10" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/safe-regex/node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=0.12" } }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "node_modules/safe-regex2": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.0.tgz", + "integrity": "sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" + "ret": "~0.5.0" }, + "bin": { + "safe-regex2": "bin/safe-regex2.js" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=10" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, - "node_modules/read": { + "node_modules/secure-json-parse": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/read/-/read-4.1.0.tgz", - "integrity": "sha512-uRfX6K+f+R8OOrYScaM3ixPY4erg69f8DN6pgTvMcA9iRc8iDhwrA4m3Yu8YYKsXJgVvum+m8PkRboZwwuLzYA==", - "dev": true, - "license": "ISC", - "dependencies": { - "mute-stream": "^2.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, - "node_modules/read-cmd-shim": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", - "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", - "dev": true, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/semver-greatest-satisfied-range": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-2.0.0.tgz", + "integrity": "sha512-lH3f6kMbwyANB7HuOWRMlLCa2itaCrZJ+SAqqkSZrZKO/cAsk2EOyaKHUtNkVLFyFW9pct22SFesFp3Z7zpA0g==", "dev": true, "license": "MIT", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "sver": "^1.8.3" }, "engines": { - "node": ">=4" + "node": ">= 10.13.0" } }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": ">=4" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", + "dev": true + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { - "locate-path": "^2.0.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">=4" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/read-pkg-up/node_modules/locate-path": { + "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^1.0.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^1.1.0" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/set-value/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, - "node_modules/readdirp": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", - "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, "engines": { - "node": ">= 14.18.0" + "node": ">= 0.10" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "node_modules/sha.js/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "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, "license": "MIT", "dependencies": { - "resolve": "^1.20.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">=8" } }, - "node_modules/redent": { + "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/redis": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-5.10.0.tgz", - "integrity": "sha512-0/Y+7IEiTgVGPrLFKy8oAEArSyEJkU0zvgV5xyi9NzNQ+SLZmyFbUsWIbgPcd4UdUh00opXGKlXJwMmsis5Byw==", - "dev": true, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { - "@redis/bloom": "5.10.0", - "@redis/client": "5.10.0", - "@redis/json": "5.10.0", - "@redis/search": "5.10.0", - "@redis/time-series": "5.10.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { - "node": ">= 18" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "dev": true, - "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "dev": true, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", "dependencies": { - "redis-errors": "^1.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" - }, - "node_modules/regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { - "is-equal-shallow": "^0.1.3" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regex-not": { + "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sigstore": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz", + "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "es6-error": "^4.0.1" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.1.0", + "@sigstore/tuf": "^4.0.1", + "@sigstore/verify": "^3.1.0" }, "engines": { - "node": ">=4" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", "dev": true, + "license": "MIT", "dependencies": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" }, "engines": { - "node": ">= 0.10" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true - }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/replace-ext": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", - "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "license": "MIT", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, "engines": { - "node": ">= 10" + "node": ">=0.10.0" } }, - "node_modules/replace-homedir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-2.0.0.tgz", - "integrity": "sha512-bgEuQQ/BHW0XkkJtawzrfzHFSN70f/3cNOiHa2QsYxqrjaC30X1k74FJ6xswVBP0sr0SpGIdVFuPwfrYziVeyw==", + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "license": "MIT", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, "engines": { - "node": ">= 10.13.0" + "node": ">=0.10.0" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, + "license": "MIT", "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "is-descriptor": "^1.0.0" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, + "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">= 0.12" + "node": ">= 0.4" } }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.2.0" + }, "engines": { - "node": ">=0.6" + "node": ">=0.10.0" } }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "bin": { - "uuid": "bin/uuid" + "license": "MIT", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "node_modules/snapdragon/node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, "license": "MIT", "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, + "node_modules/socket.io": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", "license": "MIT", "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.2.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, + "node_modules/socket.io-adapter": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.18.3" } }, - "node_modules/resolve-options": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", - "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", - "dev": true, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "dependencies": { - "value-or-function": "^4.0.0" - }, "engines": { - "node": ">= 10.13.0" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "node_modules/socket.io-client": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", "dev": true, "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, "engines": { - "node": ">=10" + "node": ">=10.0.0" } }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, + "node_modules/socket.io-parser": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", "license": "MIT", "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.0.0" } }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { - "mimic-function": "^5.0.0" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/restore-cursor/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, - "license": "ISC", + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.6" } }, - "node_modules/ret": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", - "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", - "dev": true, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { - "node": ">= 4" + "node": ">= 0.6" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "dev": true, "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 10.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">= 14" } }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" + "atomic-sleep": "^1.0.0" } }, - "node_modules/router/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "is-plain-obj": "^1.0.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=4" } }, - "node_modules/router/node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" - }, - "node_modules/router/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/run-async": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", - "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==", + "node_modules/sort-keys/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=0.10.0" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "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" + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/rxjs-compat": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.6.7.tgz", - "integrity": "sha512-szN4fK+TqBPOFBcBcsR0g2cmTTUF/vaFEOZNuSdfU8/pGFnNmmn2u8SystYXG1QMrjOPBc6XTKHMVfENDf6hHw==", - "dev": true + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", - "dev": true, - "dependencies": { - "ret": "~0.1.10" - } + "license": "MIT" }, - "node_modules/safe-regex/node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/sparkles": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz", + "integrity": "sha512-r7iW1bDw8R/cFifrD3JnQJX0K1jqT0kprL48BiBpLZLJPmAm34zsVBsK5lc7HirZYZqMW65dOXZgbAGt/I6frg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">= 10.13.0" } }, - "node_modules/safe-regex2": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", - "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], "license": "MIT", "dependencies": { - "ret": "~0.5.0" + "memory-pager": "^1.0.2" } }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/secure-json-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", - "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" + "license": "CC-BY-3.0" }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "bin": { - "semver": "bin/semver" + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/semver-greatest-satisfied-range": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-2.0.0.tgz", - "integrity": "sha512-lH3f6kMbwyANB7HuOWRMlLCa2itaCrZJ+SAqqkSZrZKO/cAsk2EOyaKHUtNkVLFyFW9pct22SFesFp3Z7zpA0g==", + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, "license": "MIT", "dependencies": { - "sver": "^1.8.3" + "through": "2" }, "engines": { - "node": ">= 10.13.0" + "node": "*" } }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "extend-shallow": "^3.0.0" }, "engines": { - "node": ">= 18" + "node": ">=0.10.0" } }, - "node_modules/send/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", "engines": { - "node": ">=6.0" + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sql-escaper": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz", + "integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==", + "dev": true, + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" } }, - "node_modules/send/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "dev": true, + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=14" } }, - "node_modules/send/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" }, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", - "dev": true + "node_modules/sshpk/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "node_modules/ssri": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", + "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", "dev": true, + "license": "ISC", "dependencies": { - "randombytes": "^2.1.0" + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "dev": true, + "license": "MIT" + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, "license": "MIT", "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "engines": { - "node": ">= 18" + "node": ">=0.10.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "dev": true + "node_modules/std-env": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", + "dev": true, + "license": "MIT" }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "duplexer": "~0.1.1", + "through": "~2.3.4" } }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", "dev": true, + "license": "MIT", "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" } }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/stream-combiner2/node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "readable-stream": "^2.0.2" } }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/stream-composer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", + "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.13.2" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "dev": true, + "license": "MIT" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "engines": { - "node": ">=0.10.0" + "node": ">=10.0.0" } }, - "node_modules/set-value/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/streamx": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", + "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", "dev": true, + "license": "MIT", "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } }, - "node_modules/sha.js": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", - "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, - "license": "(MIT AND BSD-3-Clause)", + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "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, + "license": "MIT", "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.0" - }, - "bin": { - "sha.js": "bin.js" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/shebang-regex": { + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "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, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "node_modules/string-width/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, "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "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, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "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, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "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, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/strip-ansi/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, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/sift": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", - "dev": true - }, - "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/sigstore": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz", - "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==", + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.1.0", - "@sigstore/protobuf-specs": "^0.5.0", - "@sigstore/sign": "^4.1.0", - "@sigstore/tuf": "^4.0.1", - "@sigstore/verify": "^3.1.0" - }, + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=8" } }, - "node_modules/sinon": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.1.tgz", - "integrity": "sha512-Z0NVCW45W8Mg5oC/27/+fCqIHFnW8kpkFOq0j9XJIev4Ld0mKmERaZv5DMLAb9fGCevjKwaEeIQz5+MBXfZcDw==", + "node_modules/strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^15.1.0", - "@sinonjs/samsam": "^8.0.3", - "diff": "^8.0.2", - "supports-color": "^7.2.0" + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon-chai": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", - "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", - "dev": true, - "license": "(BSD-2-Clause OR WTFPL)", - "peerDependencies": { - "chai": "^4.0.0", - "sinon": ">=4.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/sinon/node_modules/diff": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", - "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "node_modules/strip-bom-stream/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "dependencies": { + "is-utf8": "^0.2.0" + }, "engines": { - "node": ">=0.3.1" + "node": ">=0.10.0" } }, - "node_modules/slash": { + "node_modules/strip-bom-string": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "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, "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "node": ">=6" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "min-indent": "^1.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "dev": true, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.3.1" + "@tokenizer/token": "^0.3.0" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "node_modules/subscriptions-transport-ws": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", + "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", + "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md", "dev": true, + "license": "MIT", "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "backo2": "^1.0.2", + "eventemitter3": "^3.1.0", + "iterall": "^1.2.1", + "symbol-observable": "^1.0.4", + "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "graphql": "^15.7.2 || ^16.0.0" } }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "node_modules/subscriptions-transport-ws/node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", "dev": true, - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "node_modules/subscriptions-transport-ws/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "node_modules/superagent": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" }, "engines": { - "node": ">= 0.4" + "node": ">=14.18.0" } }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, - "dependencies": { - "kind-of": "^3.2.0" + "license": "MIT", + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0.0" } }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", "dev": true, + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=14.18.0" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/sver": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/sver/-/sver-1.8.4.tgz", + "integrity": "sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==", "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "optionalDependencies": { + "semver": "^6.3.0" } }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/sver/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon/node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/tar": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/socket.io": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", - "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, "license": "MIT", "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" }, "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" + "node": ">=6" } }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/socket.io-adapter/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true + "node_modules/tar-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "utf-8-validate": { - "optional": true + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/socket.io-client": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", - "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">= 6" } }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "streamx": "^2.12.5" } }, - "node_modules/socket.io-client/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", "dev": true, - "license": "MIT" - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=4" } }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "b4a": "^1.6.4" } }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10" } }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } }, - "node_modules/socks": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", - "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "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, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" + "thenify": ">= 3.1.0 < 4" }, "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" + "node": ">=0.8" } }, - "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, + "node_modules/thread-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz", + "integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "real-require": "^0.2.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=20" } }, - "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true, "license": "MIT" }, - "node_modules/sonic-boom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", - "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "license": "MIT", "dependencies": { - "atomic-sleep": "^1.0.0" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "node_modules/sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==", + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, "license": "MIT", "dependencies": { - "is-plain-obj": "^1.0.0" - }, - "engines": { - "node": ">=4" + "through2": "~2.0.0", + "xtend": "~4.0.0" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", "dev": true, + "license": "ISC", "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" } }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" }, - "node_modules/sparkles": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz", - "integrity": "sha512-r7iW1bDw8R/cFifrD3JnQJX0K1jqT0kprL48BiBpLZLJPmAm34zsVBsK5lc7HirZYZqMW65dOXZgbAGt/I6frg==", + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.13.0" + "node": ">=18" } }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "memory-pager": "^1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "dependencies": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/spawn-wrap/node_modules/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "license": "MIT", + "engines": { + "node": ">=14.0.0" } }, - "node_modules/spawn-wrap/node_modules/foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha512-3TOY+4TKV0Ml83PXJQY+JFQaHNV38lzQDIzzXYg1kWdBLenGgoZhAs0CKgzI31vi2pWEpQMq/Yi4bpKwCPkw7g==", + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, - "dependencies": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=14.14" } }, - "node_modules/spawn-wrap/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", "dev": true, + "license": "MIT", "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/spawn-wrap/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", "dev": true, + "license": "MIT", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } + "license": "MIT" }, - "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", - "dev": true + "node_modules/to-buffer/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", "dev": true, + "license": "MIT", "dependencies": { - "through": "2" + "kind-of": "^3.0.2" }, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, + "license": "MIT", "dependencies": { - "extend-shallow": "^3.0.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/sql-escaper": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.2.tgz", - "integrity": "sha512-lp+ZDVfSjHt+qAK1jXBTIXBNYnbo7gnaAGwoYTH9bE89kNkXwcu6g0WjJGRsdTKVpY1z70u3Y0IgmnBOoRybHw==", + "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, "license": "MIT", - "engines": { - "bun": ">=1.0.0", - "deno": ">=2.0.0", - "node": ">=12.0.0" + "dependencies": { + "is-number": "^7.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" - } - }, - "node_modules/sql-highlight": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", - "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", - "dev": true, - "funding": [ - "https://github.com/scriptcoded/sql-highlight?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/scriptcoded" - } - ], - "license": "MIT", "engines": { - "node": ">=14" + "node": ">=8.0" } }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "node_modules/to-regex/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, + "license": "MIT", "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/ssri": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", - "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", + "node_modules/to-regex/node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">= 0.4" } }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", - "dev": true - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "node_modules/to-through": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", + "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", "dev": true, + "license": "MIT", "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "streamx": "^2.12.5" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=0.6" } }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "dev": true, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "node_modules/stream-combiner2/node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, + "license": "MIT", "dependencies": { - "readable-stream": "^2.0.2" + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" } }, - "node_modules/stream-combiner2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/stream-combiner2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "license": "MIT", + "bin": { + "tree-kill": "cli.js" } }, - "node_modules/stream-combiner2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/stream-combiner2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/treeverse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", + "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/stream-composer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", - "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true, "license": "MIT", - "dependencies": { - "streamx": "^2.13.2" - } - }, - "node_modules/stream-exhaust": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", - "dev": true - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "dev": true - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, "engines": { - "node": ">=10.0.0" + "node": ">=8" } }, - "node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "node_modules/ts-morph": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz", + "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==", "dev": true, "license": "MIT", "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" + "@ts-morph/common": "~0.28.1", + "code-block-writer": "^13.0.3" } }, - "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "engines": { - "node": ">=0.6.19" + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "node_modules/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==", + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.3.1" } }, - "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==", + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/strip-bom": { + "node_modules/tsconfig-paths/node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/strip-bom-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", - "integrity": "sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", "dev": true, + "license": "MIT", "dependencies": { - "first-chunk-stream": "^2.0.0", - "strip-bom": "^2.0.0" + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, - "node_modules/strip-bom-stream/node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "node_modules/tuf-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-utf8": "^0.2.0" + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" }, "engines": { - "node": ">=0.10.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "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==", + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "dev": true, - "engines": { - "node": ">=6" - } + "license": "Unlicense" }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, + "license": "ISC" + }, + "node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } }, - "node_modules/strtok3": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", - "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "node": ">= 0.6" } }, - "node_modules/subscriptions-transport-ws": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", - "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, + "license": "MIT", "dependencies": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, - "peerDependencies": { - "graphql": "^15.7.2 || ^16.0.0" + "engines": { + "node": ">= 0.4" } }, - "node_modules/subscriptions-transport-ws/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typeorm": { + "version": "0.3.28", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", + "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", "dev": true, + "license": "MIT", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "ansis": "^4.2.0", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "dayjs": "^1.11.19", + "debug": "^4.4.3", + "dedent": "^1.7.0", + "dotenv": "^16.6.1", + "glob": "^10.5.0", + "reflect-metadata": "^0.2.2", + "sha.js": "^2.4.12", + "sql-highlight": "^6.1.0", + "tslib": "^2.8.1", + "uuid": "^11.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, "engines": { - "node": ">=8.3.0" + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@sap/hana-client": "^2.14.22", + "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0 || ^6.0.0", + "mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" }, "peerDependenciesMeta": { - "bufferutil": { + "@google-cloud/spanner": { "optional": true }, - "utf-8-validate": { + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { "optional": true } } }, - "node_modules/superagent": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", - "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", + "node_modules/typeorm/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, - "license": "MIT", + "license": "ISC", "dependencies": { - "component-emitter": "^1.3.1", - "cookiejar": "^2.1.4", - "debug": "^4.3.7", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.5", - "formidable": "^3.5.4", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.14.1" + "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": ">=14.18.0" + "node": ">=12" } }, - "node_modules/superagent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "node_modules/typeorm/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" + "node": ">=12" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/typeorm/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "bin": { - "mime": "cli.js" - }, "engines": { - "node": ">=4.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/superagent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/supertest": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", - "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "node_modules/typeorm/node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "dev": true, "license": "MIT", - "dependencies": { - "cookie-signature": "^1.2.2", - "methods": "^1.1.2", - "superagent": "^10.3.0" + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" }, - "engines": { - "node": ">=14.18.0" + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/supertest/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "node_modules/typeorm/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">=6.6.0" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/typeorm/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, + "license": "MIT" + }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "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" }, - "engines": { - "node": ">=8" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/typeorm/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, - "engines": { - "node": ">= 0.4" + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/sver": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/sver/-/sver-1.8.4.tgz", - "integrity": "sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==", + "node_modules/typeorm/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "MIT", - "optionalDependencies": { - "semver": "^6.3.0" - } + "license": "ISC" }, - "node_modules/sver/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "node_modules/typeorm/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "node_modules/typeorm/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, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.9" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/synckit" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "node_modules/typeorm/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, + "license": "MIT", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/typeorm/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" + "bin": { + "uuid": "dist/esm/bin/uuid" } }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/typeorm/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, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/tar-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "dependencies": { - "minipass": "^3.0.0" + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, "engines": { - "node": ">= 8" + "node": ">=0.8.0" } }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "@lukeed/csprng": "^1.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/teex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", - "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "node_modules/undertaker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-2.0.0.tgz", + "integrity": "sha512-tO/bf30wBbTsJ7go80j0RzA2rcwX6o7XPBpeFcb+jzoeb4pfMM2zUeSDIkY1AWqeZabWxaQZ/h8N9t35QKDLPQ==", "dev": true, "license": "MIT", "dependencies": { - "streamx": "^2.12.5" + "bach": "^2.0.1", + "fast-levenshtein": "^3.0.0", + "last-run": "^2.0.0", + "undertaker-registry": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/temp-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", - "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "node_modules/undertaker-registry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-2.0.0.tgz", + "integrity": "sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 10.13.0" } }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "node_modules/undertaker/node_modules/fast-levenshtein": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", + "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "b4a": "^1.6.4" + "fastest-levenshtein": "^1.0.7" } }, - "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/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" }, - "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==", + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, + "license": "MIT", "dependencies": { - "thenify": ">= 3.1.0 < 4" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" }, "engines": { - "node": ">=0.8" + "node": ">=0.10.0" } }, - "node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, "license": "MIT", - "dependencies": { - "real-require": "^0.2.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, + "license": "ISC", "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "unique-slug": "^2.0.0" } }, - "node_modules/through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "dev": true, + "license": "ISC", "dependencies": { - "through2": "~2.0.0", - "xtend": "~4.0.0" + "imurmurhash": "^0.1.4" } }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/unique-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.4.0.tgz", + "integrity": "sha512-V6QarSfeSgDipGA9EZdoIzu03ZDlOFkk+FbEP5cwgrZXN3iIkYR91IjU2EnM6rB835kGQsqHX8qncObTXV+6KA==", "dev": true, + "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "3.0.0" } }, - "node_modules/through2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } + "license": "ISC" }, - "node_modules/time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 10.0.0" } }, - "node_modules/timers-ext": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", - "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", - "dev": true, - "dependencies": { - "es5-ext": "^0.10.64", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "dev": true, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.8" } }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=0.10.0" } }, - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", - "dev": true, "dependencies": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/to-buffer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", - "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", "dev": true, "license": "MIT", "dependencies": { - "isarray": "^2.0.5", - "safe-buffer": "^5.2.1", - "typed-array-buffer": "^1.0.3" + "isarray": "1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/to-buffer/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4", + "yarn": "*" } }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "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==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" + "punycode": "^2.1.0" } }, - "node_modules/to-through": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", - "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true, + "license": "MIT" + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, "license": "MIT", "dependencies": { - "streamx": "^2.12.5" - }, - "engines": { - "node": ">=10.13.0" + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" } }, - "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, - "node_modules/token-types": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", - "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.1.0", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "bin": { + "uuid": "dist-node/bin/uuid" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } + "license": "MIT" }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "node_modules/v8flags": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", + "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", "dev": true, "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, "engines": { - "node": ">=18" + "node": ">= 10.13.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==", + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "bin": { - "tree-kill": "cli.js" + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/treeverse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", - "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", + "node_modules/validate-npm-package-name": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, + "node_modules/validator": { + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "node_modules/value-or-function": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", + "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" + "node": ">= 10.13.0" } }, - "node_modules/ts-morph": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz", - "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==", - "dev": true, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "dependencies": { - "@ts-morph/common": "~0.28.1", - "code-block-writer": "^13.0.3" + "engines": { + "node": ">= 0.8" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" } }, - "node_modules/ts-node/node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/ts-node/node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", "dev": true, "license": "MIT", "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.10" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/tuf-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", - "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", + "node_modules/vinyl-contents": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", + "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", "dev": true, "license": "MIT", "dependencies": { - "@tufjs/models": "4.1.0", - "debug": "^4.4.3", - "make-fetch-happen": "^15.0.1" + "bl": "^5.0.0", + "vinyl": "^3.0.0" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=10.13.0" } }, - "node_modules/tuf-js/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/vinyl-contents/node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/vinyl-contents/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 6" } }, - "node_modules/tuf-js/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/vinyl-contents/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 10" + } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/vinyl-contents/node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "^5.0.1" + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" }, "engines": { - "node": "*" + "node": ">=10.13.0" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/vinyl-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", + "integrity": "sha512-44i5QVLwRPbiRyuiHJ+zJXooNNRXUUifdfYIC1Gm7YTlemMgYQrZ+q1LERS6AYAN8w0xe7n9OgjEYckQjR5+4g==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^1.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "node_modules/vinyl-file/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.8" } }, - "node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/vinyl-file/node_modules/clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/vinyl-file/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "node_modules/vinyl-file/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" + "is-utf8": "^0.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true + "node_modules/vinyl-file/node_modules/vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + }, + "engines": { + "node": ">= 0.9" + } }, - "node_modules/typeorm": { - "version": "0.3.28", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", - "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", + "node_modules/vinyl-fs": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", + "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", "dev": true, "license": "MIT", "dependencies": { - "@sqltools/formatter": "^1.2.5", - "ansis": "^4.2.0", - "app-root-path": "^3.1.0", - "buffer": "^6.0.3", - "dayjs": "^1.11.19", - "debug": "^4.4.3", - "dedent": "^1.7.0", - "dotenv": "^16.6.1", - "glob": "^10.5.0", - "reflect-metadata": "^0.2.2", - "sha.js": "^2.4.12", - "sql-highlight": "^6.1.0", - "tslib": "^2.8.1", - "uuid": "^11.1.0", - "yargs": "^17.7.2" + "fs-mkdirp-stream": "^2.0.1", + "glob-stream": "^8.0.3", + "graceful-fs": "^4.2.11", + "iconv-lite": "^0.6.3", + "is-valid-glob": "^1.0.0", + "lead": "^4.0.0", + "normalize-path": "3.0.0", + "resolve-options": "^2.0.0", + "stream-composer": "^1.0.2", + "streamx": "^2.14.0", + "to-through": "^3.0.0", + "value-or-function": "^4.0.0", + "vinyl": "^3.0.1", + "vinyl-sourcemap": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-fs/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-fs/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/vinyl-fs/node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", + "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-source-map": "^2.0.0", + "graceful-fs": "^4.2.10", + "now-and-later": "^3.0.0", + "streamx": "^2.12.5", + "vinyl": "^3.0.0", + "vinyl-contents": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-sourcemap/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/vinyl-sourcemap/node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl/node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" }, "bin": { - "typeorm": "cli.js", - "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", - "typeorm-ts-node-esm": "cli-ts-node-esm.js" + "vite": "bin/vite.js" }, "engines": { - "node": ">=16.13.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://opencollective.com/typeorm" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" }, "peerDependencies": { - "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@sap/hana-client": "^2.14.22", - "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", - "ioredis": "^5.0.4", - "mongodb": "^5.8.0 || ^6.0.0", - "mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0", - "mysql2": "^2.2.5 || ^3.0.1", - "oracledb": "^6.3.0", - "pg": "^8.5.1", - "pg-native": "^3.0.0", - "pg-query-stream": "^4.0.0", - "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", - "sql.js": "^1.4.0", - "sqlite3": "^5.0.3", - "ts-node": "^10.7.0", - "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { - "@google-cloud/spanner": { - "optional": true - }, - "@sap/hana-client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "mongodb": { + "@types/node": { "optional": true }, - "mssql": { + "@vitejs/devtools": { "optional": true }, - "mysql2": { + "esbuild": { "optional": true }, - "oracledb": { + "jiti": { "optional": true }, - "pg": { + "less": { "optional": true }, - "pg-native": { + "sass": { "optional": true }, - "pg-query-stream": { + "sass-embedded": { "optional": true }, - "redis": { + "stylus": { "optional": true }, - "sql.js": { + "sugarss": { "optional": true }, - "sqlite3": { + "terser": { "optional": true }, - "ts-node": { + "tsx": { "optional": true }, - "typeorm-aurora-data-api-driver": { + "yaml": { "optional": true } } }, - "node_modules/typeorm/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/typeorm/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "node_modules/vite/node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, - "node_modules/typeorm/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" + "node_modules/vitest": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", + "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.2", + "@vitest/mocker": "4.1.2", + "@vitest/pretty-format": "4.1.2", + "@vitest/runner": "4.1.2", + "@vitest/snapshot": "4.1.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">=6.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/typeorm/node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", - "dev": true, - "license": "MIT", "peerDependencies": { - "babel-plugin-macros": "^3.1.0" + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.2", + "@vitest/browser-preview": "4.1.2", + "@vitest/browser-webdriverio": "4.1.2", + "@vitest/ui": "4.1.2", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "babel-plugin-macros": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, - "node_modules/typeorm/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/typeorm/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", "dev": true, "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "20 || >=22" } }, - "node_modules/typeorm/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/typeorm/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" + "dependencies": { + "defaults": "^1.0.3" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=14.17" + "node": ">=12" } }, - "node_modules/typescript-eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.55.0.tgz", - "integrity": "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==", + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.55.0", - "@typescript-eslint/parser": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/utils": "8.55.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": ">=18" } }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=0.8.0" + "node": ">=18" } }, - "node_modules/uid": { + "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", - "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", "dependencies": { - "@lukeed/csprng": "^1.0.0" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/uint8array-extras": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", - "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undertaker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-2.0.0.tgz", - "integrity": "sha512-tO/bf30wBbTsJ7go80j0RzA2rcwX6o7XPBpeFcb+jzoeb4pfMM2zUeSDIkY1AWqeZabWxaQZ/h8N9t35QKDLPQ==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, + "license": "MIT", "dependencies": { - "bach": "^2.0.1", - "fast-levenshtein": "^3.0.0", - "last-run": "^2.0.0", - "undertaker-registry": "^2.0.0" + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=8" } }, - "node_modules/undertaker-registry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-2.0.0.tgz", - "integrity": "sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew==", + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, - "engines": { - "node": ">= 10.13.0" + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, "license": "MIT" }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "node_modules/worker-factory": { + "version": "7.0.49", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.49.tgz", + "integrity": "sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==", "dev": true, + "license": "MIT", "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1" } }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/worker-timers": { + "version": "8.0.31", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.31.tgz", + "integrity": "sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.16", + "worker-timers-worker": "^9.0.14" } }, - "node_modules/unique-filename": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz", - "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==", + "node_modules/worker-timers-broker": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.16.tgz", + "integrity": "sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "unique-slug": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" + "@babel/runtime": "^7.29.2", + "broker-factory": "^3.1.14", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.14" } }, - "node_modules/unique-slug": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz", - "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==", + "node_modules/worker-timers-worker": { + "version": "9.0.14", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.14.tgz", + "integrity": "sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" } }, - "node_modules/unique-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", - "dev": true, - "dependencies": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=8" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "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, "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, + "license": "ISC", "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "node_modules/write-file-atomic/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, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/write-json-file": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz", + "integrity": "sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==", "dev": true, + "license": "MIT", "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.15", + "make-dir": "^2.1.0", + "pify": "^4.0.1", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.4.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "node_modules/write-json-file/node_modules/make-dir": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "license": "MIT", "dependencies": { - "isarray": "1.0.0" + "pify": "^4.0.1", + "semver": "^5.6.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "node_modules/write-json-file/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/unset-value/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "node_modules/write-json-file/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "engines": { - "node": ">=4", - "yarn": "*" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/write-json-file/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, + "license": "ISC", "dependencies": { - "punycode": "^2.1.0" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "node_modules/write-pkg": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz", + "integrity": "sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==", "dev": true, + "license": "MIT", "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "sort-keys": "^2.0.0", + "type-fest": "^0.4.1", + "write-json-file": "^3.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "node_modules/write-pkg/node_modules/type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8flags": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", - "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 10.13.0" + "node": ">=0.4.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/xss": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", + "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", "dev": true, + "license": "MIT", "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" } }, - "node_modules/validate-npm-package-name": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", - "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "node_modules/xss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } + "license": "MIT" }, - "node_modules/validator": { - "version": "13.15.20", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", - "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=0.4" } }, - "node_modules/value-or-function": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", - "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "ISC", "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, - "node_modules/vinyl": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", - "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^2.1.2", - "remove-trailing-separator": "^1.1.0", - "replace-ext": "^2.0.0", - "teex": "^1.0.1" + "license": "ISC", + "bin": { + "yaml": "bin.mjs" }, "engines": { - "node": ">=10.13.0" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, - "node_modules/vinyl-contents": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", - "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { - "bl": "^5.0.0", - "vinyl": "^3.0.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10.13.0" + "node": ">=12" } }, - "node_modules/vinyl-contents/node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/vinyl-contents/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "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" - } - ], "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "engines": { + "node": ">=6" } }, - "node_modules/vinyl-contents/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", "dev": true, "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vinyl-contents/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, + "packages/common": { + "name": "@nestjs/common", + "version": "11.1.13", "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "file-type": "21.3.0", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, - "node_modules/vinyl-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", - "integrity": "sha512-44i5QVLwRPbiRyuiHJ+zJXooNNRXUUifdfYIC1Gm7YTlemMgYQrZ+q1LERS6AYAN8w0xe7n9OgjEYckQjR5+4g==", - "dev": true, + "packages/core": { + "name": "@nestjs/core", + "version": "11.1.13", + "hasInstallScript": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "pify": "^2.3.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0", - "strip-bom-stream": "^2.0.0", - "vinyl": "^1.1.0" + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "devDependencies": { + "@nestjs/common": "11.1.13" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vinyl-file/node_modules/clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", - "dev": true - }, - "node_modules/vinyl-file/node_modules/replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", - "dev": true, - "engines": { - "node": ">= 0.4" + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } } }, - "node_modules/vinyl-file/node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", - "dev": true, + "packages/microservices": { + "name": "@nestjs/microservices", + "version": "11.1.13", + "license": "MIT", "dependencies": { - "is-utf8": "^0.2.0" + "iterare": "1.2.1", + "tslib": "2.8.1" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vinyl-file/node_modules/vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==", - "dev": true, - "dependencies": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - }, - "engines": { - "node": ">= 0.9" - } - }, - "node_modules/vinyl-fs": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", - "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fs-mkdirp-stream": "^2.0.1", - "glob-stream": "^8.0.3", - "graceful-fs": "^4.2.11", - "iconv-lite": "^0.6.3", - "is-valid-glob": "^1.0.0", - "lead": "^4.0.0", - "normalize-path": "3.0.0", - "resolve-options": "^2.0.0", - "stream-composer": "^1.0.2", - "streamx": "^2.14.0", - "to-through": "^3.0.0", - "value-or-function": "^4.0.0", - "vinyl": "^3.0.1", - "vinyl-sourcemap": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/vinyl-fs/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vinyl-sourcemap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", - "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "convert-source-map": "^2.0.0", - "graceful-fs": "^4.2.10", - "now-and-later": "^3.0.0", - "streamx": "^2.12.5", - "vinyl": "^3.0.0", - "vinyl-contents": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/vinyl/node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/walk-up-path": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", - "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", - "dev": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/worker-factory": { - "version": "7.0.44", - "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.44.tgz", - "integrity": "sha512-08AuUfWi+KeZI+KC7nU4pU/9tDeAFvE5NSWk+K9nIfuQc6UlOsZtjjeGVYVEn+DEchyXNJ5i10HCn0xRzFXEQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.6", - "fast-unique-numbers": "^9.0.22", - "tslib": "^2.8.1" - } - }, - "node_modules/worker-timers": { - "version": "8.0.23", - "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.23.tgz", - "integrity": "sha512-1BnWHNNiu5YEutgF7eVZEqNntAsij2oG0r66xDdScoY3fKGFrok2y0xA8OgG6FA+3srrmAplSY6JN5h9jV5D0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.6", - "tslib": "^2.8.1", - "worker-timers-broker": "^8.0.9", - "worker-timers-worker": "^9.0.9" - } - }, - "node_modules/worker-timers-broker": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.9.tgz", - "integrity": "sha512-WJsd7aIvu2GBTXp7IBGT1NKnt3ZbiJ2wqb7Pl4nFJXC8pek84+X68TJGVvvrqwHgHPNxSlzpU1nadhcW4PDD7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.6", - "broker-factory": "^3.1.8", - "fast-unique-numbers": "^9.0.22", - "tslib": "^2.8.1", - "worker-timers-worker": "^9.0.9" - } - }, - "node_modules/worker-timers-worker": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.9.tgz", - "integrity": "sha512-OOKTMdHbzx7FaXCW40RS8RxAqLF/R8xU5/YA7CFasDy+jBA5yQWUusSQJUFFTV2Z9ZOpnR+ZWgte/IuAqOAEVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.6", - "tslib": "^2.8.1", - "worker-factory": "^7.0.44" - } - }, - "node_modules/workerpool": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.2.tgz", - "integrity": "sha512-Xz4Nm9c+LiBHhDR5bDLnNzmj6+5F+cyEAWPMkbs2awq/dYazR/efelZzUAjB/y3kNHL+uzkHvxVVpaOfGCPV7A==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "devDependencies": { + "@nestjs/common": "11.1.13", + "@nestjs/core": "11.1.13" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "node_modules/write-json-file": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz", - "integrity": "sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-indent": "^5.0.0", - "graceful-fs": "^4.1.15", - "make-dir": "^2.1.0", - "pify": "^4.0.1", - "sort-keys": "^2.0.0", - "write-file-atomic": "^2.4.2" + "type": "opencollective", + "url": "https://opencollective.com/nest" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/write-json-file/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "peerDependencies": { + "@grpc/grpc-js": "*", + "@nats-io/transport-node": "*", + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "amqp-connection-manager": "*", + "amqplib": "*", + "cache-manager": "*", + "ioredis": "*", + "kafkajs": "*", + "mqtt": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/write-json-file/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "peerDependenciesMeta": { + "@grpc/grpc-js": { + "optional": true + }, + "@nats-io/transport-node": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + }, + "amqp-connection-manager": { + "optional": true + }, + "amqplib": { + "optional": true + }, + "cache-manager": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "kafkajs": { + "optional": true + }, + "mqtt": { + "optional": true + } } }, - "node_modules/write-pkg": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz", - "integrity": "sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==", - "dev": true, + "packages/platform-express": { + "name": "@nestjs/platform-express", + "version": "11.1.13", "license": "MIT", "dependencies": { - "sort-keys": "^2.0.0", - "type-fest": "^0.4.1", - "write-json-file": "^3.2.0" + "cors": "2.8.6", + "express": "5.2.1", + "multer": "2.0.2", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/write-pkg/node_modules/type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=6" + "devDependencies": { + "@nestjs/common": "11.1.13", + "@nestjs/core": "11.1.13" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" } }, - "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "dev": true, + "packages/platform-fastify": { + "name": "@nestjs/platform-fastify", + "version": "11.1.13", "license": "MIT", - "engines": { - "node": ">=10.0.0" + "dependencies": { + "@fastify/cors": "11.2.0", + "@fastify/formbody": "8.0.2", + "fast-querystring": "1.1.2", + "fastify": "5.7.4", + "fastify-plugin": "5.1.0", + "find-my-way": "9.4.0", + "light-my-request": "6.6.0", + "path-to-regexp": "8.3.0", + "reusify": "1.1.0", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "@fastify/static": "^8.0.0 || ^9.0.0", + "@fastify/view": "^10.0.0 || ^11.0.0", + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" }, "peerDependenciesMeta": { - "bufferutil": { + "@fastify/static": { "optional": true }, - "utf-8-validate": { + "@fastify/view": { "optional": true } } }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/xss": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", - "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", - "dev": true, + "packages/platform-socket.io": { + "name": "@nestjs/platform-socket.io", + "version": "11.1.13", "license": "MIT", "dependencies": { - "commander": "^2.20.3", - "cssfilter": "0.0.10" - }, - "bin": { - "xss": "bin/xss" + "socket.io": "4.8.3", + "tslib": "2.8.1" }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" }, - "engines": { - "node": ">= 14.6" + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "rxjs": "^7.1.0" } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, + "packages/platform-ws": { + "name": "@nestjs/platform-ws", + "version": "11.1.13", + "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "tslib": "2.8.1", + "ws": "8.19.0" }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "rxjs": "^7.1.0" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" + "packages/testing": { + "name": "@nestjs/testing", + "version": "11.1.13", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" + "type": "opencollective", + "url": "https://opencollective.com/nest" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } } }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "dev": true, + "packages/websockets": { + "name": "@nestjs/websockets", + "version": "11.1.13", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.8.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "devDependencies": { + "@nestjs/common": "11.1.13", + "@nestjs/core": "11.1.13" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/platform-socket.io": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/platform-socket.io": { + "optional": true + } } } } diff --git a/package.json b/package.json index b941217e0a4..084a4ab9124 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,10 @@ "url": "https://opencollective.com/nest" }, "license": "MIT", + "type": "module", + "workspaces": [ + "packages/*" + ], "author": "Kamil Mysliwiec", "scripts": { "build": "tsc -b -v packages", @@ -23,31 +27,27 @@ "clean": "tsc -b --clean packages", "move:samples": "gulp move:samples", "move:node_modules": "gulp move:node_modules", - "build:samples": "gulp install:samples && npm run build && npm run move:samples && gulp build:samples && gulp test:samples && gulp test:e2e:samples", + "build:samples": "gulp install:samples && npm run build && npm run move:samples && gulp build:samples", "codechecks:benchmarks": "codechecks ./tools/benchmarks/check-benchmarks.ts", - "coverage": "nyc report --reporter=text-lcov | coveralls -v", + "coverage": "vitest run --coverage --config vitest.config.coverage.mts || true", "format": "prettier \"**/*.ts\" \"packages/**/*.json\" --ignore-path ./.prettierignore --write && git status", "postinstall": "opencollective", - "test": "mocha packages/**/*.spec.ts", - "test:dev": "mocha -w --watch-files \"packages\" packages/**/*.spec.ts", - "pretest:cov": "npm run clean", - "test:cov": "nyc mocha packages/**/*.spec.ts --reporter spec", - "test:integration": "mocha --reporter-option maxDiffSize=0 \"integration/*/{,!(node_modules)/**/}/*.spec.ts\"", + "test": "vitest run", + "test:dev": "vitest", + "test:cov": "vitest run --coverage --config vitest.config.coverage.mts || true", + "test:integration": "vitest run --config vitest.config.integration.mts", "test:docker:up": "docker-compose -f integration/docker-compose.yml up -d", "test:docker:down": "docker-compose -f integration/docker-compose.yml down", - "lint": "concurrently 'npm run lint:packages' 'npm run lint:integration' 'npm run lint:spec'", - "lint:fix": "concurrently 'npm run lint:packages -- --fix' 'npm run lint:integration -- --fix' 'npm run lint:spec -- --fix'", - "lint:integration": "node --max-old-space-size=4096 ./node_modules/.bin/eslint 'integration/**/*.ts'", - "lint:packages": "node --max-old-space-size=4096 ./node_modules/.bin/eslint 'packages/**/**.ts' --ignore-pattern 'packages/**/*.spec.ts'", - "lint:spec": "node --max-old-space-size=4096 ./node_modules/.bin/eslint 'packages/**/**.spec.ts'", - "lint:ci": "concurrently 'npm run lint:packages' 'npm run lint:spec'", + "lint": "oxlint packages integration", + "lint:fix": "oxlint --fix packages integration", + "lint:ci": "oxlint packages", "prerelease": "gulp copy-misc", "publish": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --exact -m \"chore(release): publish %s release\"", "prepublishOnly": "npm run changelog | pbcopy", "publish:beta": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=beta -m \"chore(release): publish %s release\"", - "publish:next": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=next --skip-git -m \"chore(release): publish %s release\"", + "publish:next": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=next --no-git-tag-version --no-push -m \"chore(release): publish %s release\"", "publish:rc": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=rc -m \"chore(release): publish %s release\"", - "publish:test": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --npm-tag=test --skip-git -m \"chore(release): publish %s release\"", + "publish:test": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --npm-tag=test --no-git-tag-version --no-push -m \"chore(release): publish %s release\"", "prepare": "husky" }, "lint-staged": { @@ -84,8 +84,6 @@ "@as-integrations/express5": "1.1.2", "@commitlint/cli": "20.4.1", "@commitlint/config-angular": "20.4.1", - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@fastify/cors": "11.2.0", "@fastify/formbody": "8.0.2", "@fastify/middie": "9.1.0", @@ -94,44 +92,33 @@ "@fastify/view": "11.1.1", "@grpc/grpc-js": "1.14.3", "@grpc/proto-loader": "0.8.0", + "@nats-io/transport-node": "^3.0.2", "@nestjs/apollo": "13.2.4", "@nestjs/graphql": "13.2.4", "@nestjs/mongoose": "11.0.4", "@nestjs/typeorm": "11.0.0", "@types/amqplib": "0.10.8", - "@types/chai": "4.3.20", - "@types/chai-as-promised": "7.1.8", "@types/cors": "2.8.19", - "@types/eslint__js": "8.42.3", "@types/express": "5.0.6", "@types/gulp": "4.0.18", "@types/http-errors": "2.0.5", - "@types/mocha": "10.0.10", - "@types/node": "25.2.3", - "@types/sinon": "21.0.0", + "@types/node": "25.2.1", "@types/supertest": "6.0.3", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.55.0", - "@typescript-eslint/parser": "8.55.0", + "@vitest/coverage-istanbul": "4.1.2", + "@vitest/coverage-v8": "4.1.2", "amqp-connection-manager": "5.0.0", "amqplib": "0.10.9", "body-parser": "2.2.2", "cache-manager": "7.2.8", - "chai": "4.5.0", - "chai-as-promised": "7.1.2", - "concurrently": "9.2.1", "conventional-changelog": "7.1.1", "coveralls": "3.1.1", "delete-empty": "3.0.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.5", "eventsource": "4.1.0", "fancy-log": "2.0.0", "fastify": "5.7.4", "fastify-plugin": "5.1.0", "find-my-way": "9.4.0", - "globals": "17.3.0", "graphql": "16.12.0", "graphql-subscriptions": "3.0.0", "gulp": "5.0.1", @@ -149,26 +136,24 @@ "light-my-request": "6.6.0", "lint-staged": "16.2.7", "markdown-table": "2.0.0", - "mocha": "11.7.5", - "mongoose": "9.2.1", + "mongoose": "9.1.6", "mqtt": "5.15.0", "multer": "2.0.2", "mysql2": "3.17.1", - "nats": "2.29.3", - "nyc": "14.1.1", - "prettier": "^3.7.4", + "oxlint": "1.58.0", + "prettier": "3.7.4", "redis": "5.10.0", "reusify": "1.1.0", "rxjs-compat": "6.6.7", - "sinon": "21.0.1", - "sinon-chai": "3.7.0", "socket.io-client": "4.8.3", "supertest": "7.2.2", "ts-morph": "27.0.2", "ts-node": "10.9.2", + "tsx": "4.19.2", "typeorm": "0.3.28", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0", + "typescript": "6.0.2", + "vite": "8.0.3", + "vitest": "4.1.2", "ws": "8.19.0" }, "engines": { @@ -190,53 +175,6 @@ "type: code style": "Code style tweaks", "dependencies": "Dependencies" } - }, - "nyc": { - "include": [ - "packages/**/*.ts" - ], - "exclude": [ - "**/*.js", - "**/*.d.ts", - "**/*.spec.ts", - "packages/**/adapters/*.ts", - "packages/**/nest-*.ts", - "packages/**/test/**/*.ts", - "packages/core/errors/**/*", - "packages/common/exceptions/*.ts", - "packages/common/utils/load-package.util.ts", - "packages/microservices/exceptions/", - "packages/microservices/microservices-module.ts", - "packages/core/middleware/middleware-module.ts", - "packages/core/discovery/discovery-service.ts", - "packages/core/injector/module-ref.ts", - "packages/core/injector/instance-links-host.ts", - "packages/core/helpers/context-id-factory.ts", - "packages/websockets/socket-module.ts", - "packages/common/cache/**/*", - "packages/common/serializer/**/*", - "packages/common/services/*.ts" - ], - "extension": [ - ".ts" - ], - "require": [ - "ts-node/register" - ], - "reporter": [ - "text-summary", - "html" - ], - "sourceMap": true, - "instrument": true - }, - "mocha": { - "require": [ - "ts-node/register", - "tsconfig-paths/register", - "node_modules/reflect-metadata/Reflect.js", - "hooks/mocha-init-hook.ts" - ], - "exit": true } } + diff --git a/packages/common/decorators/core/catch.decorator.ts b/packages/common/decorators/core/catch.decorator.ts index cd3cdb161fd..cd306e916d2 100644 --- a/packages/common/decorators/core/catch.decorator.ts +++ b/packages/common/decorators/core/catch.decorator.ts @@ -1,5 +1,5 @@ -import { CATCH_WATERMARK, FILTER_CATCH_EXCEPTIONS } from '../../constants'; -import { Type, Abstract } from '../../interfaces'; +import { CATCH_WATERMARK, FILTER_CATCH_EXCEPTIONS } from '../../constants.js'; +import { Type, Abstract } from '../../interfaces/index.js'; /** * Decorator that marks a class as a Nest exception filter. An exception filter diff --git a/packages/common/decorators/core/controller.decorator.ts b/packages/common/decorators/core/controller.decorator.ts index b89dd6454f5..222924460a6 100644 --- a/packages/common/decorators/core/controller.decorator.ts +++ b/packages/common/decorators/core/controller.decorator.ts @@ -4,9 +4,9 @@ import { PATH_METADATA, SCOPE_OPTIONS_METADATA, VERSION_METADATA, -} from '../../constants'; -import { ScopeOptions, VersionOptions } from '../../interfaces'; -import { isString, isUndefined } from '../../utils/shared.utils'; +} from '../../constants.js'; +import { ScopeOptions, VersionOptions } from '../../interfaces/index.js'; +import { isString, isUndefined } from '../../utils/shared.utils.js'; /** * Interface defining options that can be passed to `@Controller()` decorator diff --git a/packages/common/decorators/core/dependencies.decorator.ts b/packages/common/decorators/core/dependencies.decorator.ts index c08a33a283b..b86ede5967e 100644 --- a/packages/common/decorators/core/dependencies.decorator.ts +++ b/packages/common/decorators/core/dependencies.decorator.ts @@ -1,4 +1,4 @@ -import { PARAMTYPES_METADATA } from '../../constants'; +import { PARAMTYPES_METADATA } from '../../constants.js'; export function flatten = any>( arr: T, diff --git a/packages/common/decorators/core/exception-filters.decorator.ts b/packages/common/decorators/core/exception-filters.decorator.ts index 27bc387b9b6..5faca3886be 100644 --- a/packages/common/decorators/core/exception-filters.decorator.ts +++ b/packages/common/decorators/core/exception-filters.decorator.ts @@ -1,8 +1,8 @@ -import { EXCEPTION_FILTERS_METADATA } from '../../constants'; -import { ExceptionFilter } from '../../index'; -import { extendArrayMetadata } from '../../utils/extend-metadata.util'; -import { isFunction } from '../../utils/shared.utils'; -import { validateEach } from '../../utils/validate-each.util'; +import { EXCEPTION_FILTERS_METADATA } from '../../constants.js'; +import { ExceptionFilter } from '../../index.js'; +import { extendArrayMetadata } from '../../utils/extend-metadata.util.js'; +import { isFunction } from '../../utils/shared.utils.js'; +import { validateEach } from '../../utils/validate-each.util.js'; /** * Decorator that binds exception filters to the scope of the controller or diff --git a/packages/common/decorators/core/index.ts b/packages/common/decorators/core/index.ts index c68c7744ffe..df92e2d0800 100644 --- a/packages/common/decorators/core/index.ts +++ b/packages/common/decorators/core/index.ts @@ -1,14 +1,14 @@ -export * from './bind.decorator'; -export * from './catch.decorator'; -export * from './controller.decorator'; -export * from './dependencies.decorator'; -export * from './exception-filters.decorator'; -export * from './inject.decorator'; -export * from './injectable.decorator'; -export * from './optional.decorator'; -export * from './set-metadata.decorator'; -export * from './use-guards.decorator'; -export * from './use-interceptors.decorator'; -export * from './use-pipes.decorator'; -export * from './apply-decorators'; -export * from './version.decorator'; +export * from './bind.decorator.js'; +export * from './catch.decorator.js'; +export * from './controller.decorator.js'; +export * from './dependencies.decorator.js'; +export * from './exception-filters.decorator.js'; +export * from './inject.decorator.js'; +export * from './injectable.decorator.js'; +export * from './optional.decorator.js'; +export * from './set-metadata.decorator.js'; +export * from './use-guards.decorator.js'; +export * from './use-interceptors.decorator.js'; +export * from './use-pipes.decorator.js'; +export * from './apply-decorators.js'; +export * from './version.decorator.js'; diff --git a/packages/common/decorators/core/inject.decorator.ts b/packages/common/decorators/core/inject.decorator.ts index f8bedbd63f7..77ea8328add 100644 --- a/packages/common/decorators/core/inject.decorator.ts +++ b/packages/common/decorators/core/inject.decorator.ts @@ -2,9 +2,9 @@ import { PARAMTYPES_METADATA, PROPERTY_DEPS_METADATA, SELF_DECLARED_DEPS_METADATA, -} from '../../constants'; -import { ForwardReference, InjectionToken } from '../../interfaces'; -import { isUndefined } from '../../utils/shared.utils'; +} from '../../constants.js'; +import { ForwardReference, InjectionToken } from '../../interfaces/index.js'; +import { isUndefined } from '../../utils/shared.utils.js'; /** * Decorator that marks a constructor parameter as a target for diff --git a/packages/common/decorators/core/injectable.decorator.ts b/packages/common/decorators/core/injectable.decorator.ts index ff7ae143c59..5c852fad617 100644 --- a/packages/common/decorators/core/injectable.decorator.ts +++ b/packages/common/decorators/core/injectable.decorator.ts @@ -1,7 +1,10 @@ import { uid } from 'uid'; -import { INJECTABLE_WATERMARK, SCOPE_OPTIONS_METADATA } from '../../constants'; -import { ScopeOptions } from '../../interfaces/scope-options.interface'; -import { Type } from '../../interfaces/type.interface'; +import { + INJECTABLE_WATERMARK, + SCOPE_OPTIONS_METADATA, +} from '../../constants.js'; +import { ScopeOptions } from '../../interfaces/scope-options.interface.js'; +import { Type } from '../../interfaces/type.interface.js'; /** * Defines the injection scope. diff --git a/packages/common/decorators/core/optional.decorator.ts b/packages/common/decorators/core/optional.decorator.ts index 0d94c20f000..62ca4ec9fb7 100644 --- a/packages/common/decorators/core/optional.decorator.ts +++ b/packages/common/decorators/core/optional.decorator.ts @@ -1,8 +1,8 @@ import { OPTIONAL_DEPS_METADATA, OPTIONAL_PROPERTY_DEPS_METADATA, -} from '../../constants'; -import { isUndefined } from '../../utils/shared.utils'; +} from '../../constants.js'; +import { isUndefined } from '../../utils/shared.utils.js'; /** * Parameter decorator for an injected dependency marking the @@ -20,12 +20,12 @@ import { isUndefined } from '../../utils/shared.utils'; export function Optional(): PropertyDecorator & ParameterDecorator { return (target: object, key: string | symbol | undefined, index?: number) => { if (!isUndefined(index)) { - const args = Reflect.getMetadata(OPTIONAL_DEPS_METADATA, target) || []; + const args = Reflect.getOwnMetadata(OPTIONAL_DEPS_METADATA, target) || []; Reflect.defineMetadata(OPTIONAL_DEPS_METADATA, [...args, index], target); return; } const properties = - Reflect.getMetadata( + Reflect.getOwnMetadata( OPTIONAL_PROPERTY_DEPS_METADATA, target.constructor, ) || []; diff --git a/packages/common/decorators/core/use-guards.decorator.ts b/packages/common/decorators/core/use-guards.decorator.ts index c3fb7cd2fd7..52fdc6c367a 100644 --- a/packages/common/decorators/core/use-guards.decorator.ts +++ b/packages/common/decorators/core/use-guards.decorator.ts @@ -1,8 +1,8 @@ -import { GUARDS_METADATA } from '../../constants'; -import { CanActivate } from '../../interfaces'; -import { extendArrayMetadata } from '../../utils/extend-metadata.util'; -import { isFunction } from '../../utils/shared.utils'; -import { validateEach } from '../../utils/validate-each.util'; +import { GUARDS_METADATA } from '../../constants.js'; +import { CanActivate } from '../../interfaces/index.js'; +import { extendArrayMetadata } from '../../utils/extend-metadata.util.js'; +import { isFunction } from '../../utils/shared.utils.js'; +import { validateEach } from '../../utils/validate-each.util.js'; /** * Decorator that binds guards to the scope of the controller or method, diff --git a/packages/common/decorators/core/use-interceptors.decorator.ts b/packages/common/decorators/core/use-interceptors.decorator.ts index 474c47a6d13..b4e0e146269 100644 --- a/packages/common/decorators/core/use-interceptors.decorator.ts +++ b/packages/common/decorators/core/use-interceptors.decorator.ts @@ -1,8 +1,8 @@ -import { INTERCEPTORS_METADATA } from '../../constants'; -import { NestInterceptor } from '../../interfaces'; -import { extendArrayMetadata } from '../../utils/extend-metadata.util'; -import { isFunction } from '../../utils/shared.utils'; -import { validateEach } from '../../utils/validate-each.util'; +import { INTERCEPTORS_METADATA } from '../../constants.js'; +import { NestInterceptor } from '../../interfaces/index.js'; +import { extendArrayMetadata } from '../../utils/extend-metadata.util.js'; +import { isFunction } from '../../utils/shared.utils.js'; +import { validateEach } from '../../utils/validate-each.util.js'; /** * Decorator that binds interceptors to the scope of the controller or method, diff --git a/packages/common/decorators/core/use-pipes.decorator.ts b/packages/common/decorators/core/use-pipes.decorator.ts index b9ead653f9e..72c66d8242f 100644 --- a/packages/common/decorators/core/use-pipes.decorator.ts +++ b/packages/common/decorators/core/use-pipes.decorator.ts @@ -1,8 +1,8 @@ -import { PIPES_METADATA } from '../../constants'; -import { PipeTransform } from '../../interfaces/index'; -import { extendArrayMetadata } from '../../utils/extend-metadata.util'; -import { isFunction } from '../../utils/shared.utils'; -import { validateEach } from '../../utils/validate-each.util'; +import { PIPES_METADATA } from '../../constants.js'; +import { PipeTransform } from '../../interfaces/index.js'; +import { extendArrayMetadata } from '../../utils/extend-metadata.util.js'; +import { isFunction } from '../../utils/shared.utils.js'; +import { validateEach } from '../../utils/validate-each.util.js'; /** * Decorator that binds pipes to the scope of the controller or method, diff --git a/packages/common/decorators/core/version.decorator.ts b/packages/common/decorators/core/version.decorator.ts index 9ab8fc88783..97f179cbdec 100644 --- a/packages/common/decorators/core/version.decorator.ts +++ b/packages/common/decorators/core/version.decorator.ts @@ -1,5 +1,5 @@ -import { VERSION_METADATA } from '../../constants'; -import { VersionValue } from '../../interfaces/version-options.interface'; +import { VERSION_METADATA } from '../../constants.js'; +import { VersionValue } from '../../interfaces/version-options.interface.js'; /** * Sets the version of the endpoint to the passed version diff --git a/packages/common/decorators/http/create-route-param-metadata.decorator.ts b/packages/common/decorators/http/create-route-param-metadata.decorator.ts index 62f90b7f627..4ea8993827f 100644 --- a/packages/common/decorators/http/create-route-param-metadata.decorator.ts +++ b/packages/common/decorators/http/create-route-param-metadata.decorator.ts @@ -1,15 +1,17 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec'; import { uid } from 'uid'; -import { ROUTE_ARGS_METADATA } from '../../constants'; -import { PipeTransform } from '../../index'; -import { Type } from '../../interfaces'; -import { CustomParamFactory } from '../../interfaces/features/custom-route-param-factory.interface'; -import { assignCustomParameterMetadata } from '../../utils/assign-custom-metadata.util'; -import { isFunction, isNil } from '../../utils/shared.utils'; +import { ROUTE_ARGS_METADATA } from '../../constants.js'; +import { PipeTransform } from '../../index.js'; +import { CustomParamFactory } from '../../interfaces/features/custom-route-param-factory.interface.js'; +import { Type } from '../../interfaces/index.js'; +import { assignCustomParameterMetadata } from '../../utils/assign-custom-metadata.util.js'; +import { isFunction, isNil } from '../../utils/shared.utils.js'; +import { ParameterDecoratorOptions } from './route-params.decorator.js'; export type ParamDecoratorEnhancer = ParameterDecorator; /** - * Defines HTTP route param decorator + * Defines route param decorator * * @param factory * @param enhancers @@ -20,12 +22,22 @@ export function createParamDecorator( factory: CustomParamFactory, enhancers: ParamDecoratorEnhancer[] = [], ): ( - ...dataOrPipes: (Type | PipeTransform | FactoryData)[] + ...dataOrPipes: ( + | Type + | PipeTransform + | FactoryData + | ParameterDecoratorOptions + )[] ) => ParameterDecorator { const paramtype = uid(21); return ( data?, - ...pipes: (Type | PipeTransform | FactoryData)[] + ...pipes: ( + | Type + | PipeTransform + | FactoryData + | ParameterDecoratorOptions + )[] ): ParameterDecorator => (target, key, index) => { const args = @@ -39,10 +51,56 @@ export function createParamDecorator( isFunction(pipe.prototype.transform)) || isFunction(pipe.transform)); + const isParameterDecoratorOptions = (value: any): boolean => + value && + typeof value === 'object' && + !isPipe(value) && + ('schema' in value || 'pipes' in value); + const hasParamData = isNil(data) || !isPipe(data); const paramData = hasParamData ? (data as any) : undefined; const paramPipes = hasParamData ? pipes : [data, ...pipes]; + // Check if data itself is an options object (when used as the first and only argument) + const isDataOptions = + hasParamData && + !isNil(data) && + paramPipes.length === 0 && + isParameterDecoratorOptions(data); + + // Check if the last pipe argument is actually an options object + const lastPipeArg = + paramPipes.length > 0 ? paramPipes[paramPipes.length - 1] : undefined; + const isLastPipeOptions = + !isDataOptions && isParameterDecoratorOptions(lastPipeArg); + + let finalData: any; + let finalSchema: StandardSchemaV1 | undefined; + let finalPipes: (Type | PipeTransform | FactoryData)[]; + + if (isDataOptions) { + const opts = data as unknown as ParameterDecoratorOptions; + finalData = undefined; + finalSchema = opts.schema; + finalPipes = (opts.pipes ?? []) as any[]; + } else if (isLastPipeOptions) { + const opts = lastPipeArg as unknown as ParameterDecoratorOptions; + finalData = paramData; + finalSchema = opts.schema; + finalPipes = [ + ...paramPipes.slice(0, -1), + ...((opts.pipes ?? []) as any[]), + ]; + } else { + finalData = paramData; + finalSchema = undefined; + finalPipes = paramPipes as ( + | Type + | PipeTransform + | FactoryData + )[]; + } + Reflect.defineMetadata( ROUTE_ARGS_METADATA, assignCustomParameterMetadata( @@ -50,8 +108,9 @@ export function createParamDecorator( paramtype, index, factory, - paramData, - ...(paramPipes as PipeTransform[]), + finalData, + finalSchema, + ...(finalPipes as PipeTransform[]), ), target.constructor, key!, diff --git a/packages/common/decorators/http/header.decorator.ts b/packages/common/decorators/http/header.decorator.ts index 875ce84728f..8b491c157b0 100644 --- a/packages/common/decorators/http/header.decorator.ts +++ b/packages/common/decorators/http/header.decorator.ts @@ -1,5 +1,5 @@ -import { HEADERS_METADATA } from '../../constants'; -import { extendArrayMetadata } from '../../utils/extend-metadata.util'; +import { HEADERS_METADATA } from '../../constants.js'; +import { extendArrayMetadata } from '../../utils/extend-metadata.util.js'; /** * Request method Decorator. Sets a response header. diff --git a/packages/common/decorators/http/http-code.decorator.ts b/packages/common/decorators/http/http-code.decorator.ts index fc3971be0a0..a1dddee74f7 100644 --- a/packages/common/decorators/http/http-code.decorator.ts +++ b/packages/common/decorators/http/http-code.decorator.ts @@ -1,4 +1,4 @@ -import { HTTP_CODE_METADATA } from '../../constants'; +import { HTTP_CODE_METADATA } from '../../constants.js'; /** * Request method Decorator. Defines the HTTP response status code. Overrides diff --git a/packages/common/decorators/http/index.ts b/packages/common/decorators/http/index.ts index 39b61bf003b..7a03af73c03 100644 --- a/packages/common/decorators/http/index.ts +++ b/packages/common/decorators/http/index.ts @@ -1,8 +1,8 @@ -export * from './request-mapping.decorator'; -export * from './route-params.decorator'; -export * from './http-code.decorator'; -export * from './create-route-param-metadata.decorator'; -export * from './render.decorator'; -export * from './header.decorator'; -export * from './redirect.decorator'; -export * from './sse.decorator'; +export * from './request-mapping.decorator.js'; +export * from './route-params.decorator.js'; +export * from './http-code.decorator.js'; +export * from './create-route-param-metadata.decorator.js'; +export * from './render.decorator.js'; +export * from './header.decorator.js'; +export * from './redirect.decorator.js'; +export * from './sse.decorator.js'; diff --git a/packages/common/decorators/http/redirect.decorator.ts b/packages/common/decorators/http/redirect.decorator.ts index e03af9dfd01..ddd433ed82d 100644 --- a/packages/common/decorators/http/redirect.decorator.ts +++ b/packages/common/decorators/http/redirect.decorator.ts @@ -1,4 +1,4 @@ -import { REDIRECT_METADATA } from '../../constants'; +import { REDIRECT_METADATA } from '../../constants.js'; /** * Redirects request to the specified URL. diff --git a/packages/common/decorators/http/render.decorator.ts b/packages/common/decorators/http/render.decorator.ts index 92d2f8d204b..810f3c14b3a 100644 --- a/packages/common/decorators/http/render.decorator.ts +++ b/packages/common/decorators/http/render.decorator.ts @@ -1,4 +1,4 @@ -import { RENDER_METADATA } from '../../constants'; +import { RENDER_METADATA } from '../../constants.js'; /** * Route handler method Decorator. Defines a template to be rendered by the controller. diff --git a/packages/common/decorators/http/request-mapping.decorator.ts b/packages/common/decorators/http/request-mapping.decorator.ts index e707883cf46..45ad1b87d78 100644 --- a/packages/common/decorators/http/request-mapping.decorator.ts +++ b/packages/common/decorators/http/request-mapping.decorator.ts @@ -1,5 +1,5 @@ -import { METHOD_METADATA, PATH_METADATA } from '../../constants'; -import { RequestMethod } from '../../enums/request-method.enum'; +import { METHOD_METADATA, PATH_METADATA } from '../../constants.js'; +import { RequestMethod } from '../../enums/request-method.enum.js'; export interface RequestMappingMetadata { path?: string | string[]; diff --git a/packages/common/decorators/http/route-params.decorator.ts b/packages/common/decorators/http/route-params.decorator.ts index ddc3f9160a0..2a0f4a06823 100644 --- a/packages/common/decorators/http/route-params.decorator.ts +++ b/packages/common/decorators/http/route-params.decorator.ts @@ -1,11 +1,28 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec'; import { RESPONSE_PASSTHROUGH_METADATA, ROUTE_ARGS_METADATA, -} from '../../constants'; -import { RouteParamtypes } from '../../enums/route-paramtypes.enum'; -import { PipeTransform } from '../../index'; -import { Type } from '../../interfaces'; -import { isNil, isString } from '../../utils/shared.utils'; +} from '../../constants.js'; +import { RouteParamtypes } from '../../enums/route-paramtypes.enum.js'; +import { PipeTransform } from '../../index.js'; +import { Type } from '../../interfaces/index.js'; +import { isNil, isString } from '../../utils/shared.utils.js'; + +/** + * The options that can be passed to a handler's parameter decorator, such as `@Query()`, `@Body()`, and others. + * These options allow you to specify a schema for validation and transformation, as well as any pipes to apply to the parameter. + */ +export interface ParameterDecoratorOptions { + /** + * The schema to use to retrieve within the pipes, + * to, for example, validate the parameter against the schema or to apply transformations based on the schema. + */ + schema?: StandardSchemaV1; + /** + * The list of pipes to apply to the parameter. + */ + pipes?: (Type | PipeTransform)[]; +} /** * The `@Response()`/`@Res` parameter decorator options. @@ -31,15 +48,17 @@ export function assignMetadata( args: TArgs, paramtype: TParamtype, index: number, - data?: ParamData, - ...pipes: (Type | PipeTransform)[] + options: { + data?: ParamData; + } & ParameterDecoratorOptions, ) { return { ...args, [`${paramtype as string}:${index}`]: { index, - data, - pipes, + data: options.data, + pipes: options.pipes ?? [], + ...(options.schema !== undefined && { schema: options.schema }), }, }; } @@ -56,7 +75,9 @@ function createRouteParamDecorator(paramtype: RouteParamtypes) { args, paramtype, index, - data, + { + data, + }, ), target.constructor, key!, @@ -66,20 +87,27 @@ function createRouteParamDecorator(paramtype: RouteParamtypes) { const createPipesRouteParamDecorator = (paramtype: RouteParamtypes) => - ( - data?: any, - ...pipes: (Type | PipeTransform)[] - ): ParameterDecorator => + ({ + data, + pipes, + schema, + }: ParameterDecoratorOptions & { data?: unknown }): ParameterDecorator => (target, key, index) => { const args = Reflect.getMetadata(ROUTE_ARGS_METADATA, target.constructor, key!) || {}; const hasParamData = isNil(data) || isString(data); const paramData = hasParamData ? data : undefined; - const paramPipes = hasParamData ? pipes : [data, ...pipes]; + const paramPipes = hasParamData + ? (pipes ?? []) + : [data as Type | PipeTransform, ...(pipes ?? [])]; Reflect.defineMetadata( ROUTE_ARGS_METADATA, - assignMetadata(args, paramtype, index, paramData!, ...paramPipes), + assignMetadata(args, paramtype, index, { + data: paramData!, + pipes: paramPipes, + schema, + }), target.constructor, key!, ); @@ -242,10 +270,10 @@ export function UploadedFile( fileKey?: string | (Type | PipeTransform), ...pipes: (Type | PipeTransform)[] ): ParameterDecorator { - return createPipesRouteParamDecorator(RouteParamtypes.FILE)( - fileKey, - ...pipes, - ); + return createPipesRouteParamDecorator(RouteParamtypes.FILE)({ + data: fileKey, + pipes, + }); } /** @@ -303,10 +331,9 @@ export function UploadedFiles( export function UploadedFiles( ...pipes: (Type | PipeTransform)[] ): ParameterDecorator { - return createPipesRouteParamDecorator(RouteParamtypes.FILES)( - undefined, - ...pipes, - ); + return createPipesRouteParamDecorator(RouteParamtypes.FILES)({ + pipes, + }); } /** @@ -399,6 +426,47 @@ export function Query( * ``` * * @param property name of single property to extract from the `query` object + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @see [Request object](https://docs.nestjs.com/controllers#request-object) + * + * @publicApi + */ +export function Query( + property: string, + options: ParameterDecoratorOptions, +): ParameterDecorator; +/** + * Route handler parameter decorator. Extracts the `query` + * property from the `req` object and populates the decorated + * parameter with the value of `query`. May also apply pipes to the bound + * query parameter. + * + * For example: + * ```typescript + * async find(@Query({ schema: z.object({ user: z.string() }) }) query) + * ``` + * + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @see [Request object](https://docs.nestjs.com/controllers#request-object) + * + * @publicApi + */ +export function Query(options: ParameterDecoratorOptions): ParameterDecorator; +/** + * Route handler parameter decorator. Extracts the `query` + * property from the `req` object and populates the decorated + * parameter with the value of `query`. May also apply pipes to the bound + * query parameter. + * + * For example: + * ```typescript + * async find(@Query('user') user: string) + * ``` + * + * @param property name of single property to extract from the `query` object + * @param optionsOrPipe one or more pipes to apply to the bound query parameter or options object * @param pipes one or more pipes to apply to the bound query parameter * * @see [Request object](https://docs.nestjs.com/controllers#request-object) @@ -406,13 +474,44 @@ export function Query( * @publicApi */ export function Query( - property?: string | (Type | PipeTransform), + property?: + | string + | (Type | PipeTransform) + | ParameterDecoratorOptions, + optionsOrPipe?: + | ParameterDecoratorOptions + | Type + | PipeTransform, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator { - return createPipesRouteParamDecorator(RouteParamtypes.QUERY)( - property, - ...pipes, - ); + const isPropertyOptions = + property && + typeof property === 'object' && + !('transform' in property) && + ('schema' in property || 'pipes' in property); + + if (isPropertyOptions) { + return createPipesRouteParamDecorator(RouteParamtypes.QUERY)({ + pipes: property.pipes, + schema: property.schema, + }); + } + + const isOptions = + optionsOrPipe && + typeof optionsOrPipe === 'object' && + ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe); + const actualPipes = isOptions + ? optionsOrPipe.pipes + : ([optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]); + return createPipesRouteParamDecorator(RouteParamtypes.QUERY)({ + data: property, + pipes: actualPipes, + schema: isOptions ? optionsOrPipe.schema : undefined, + }); } /** @@ -430,7 +529,6 @@ export function Query( * @publicApi */ export function Body(): ParameterDecorator; - /** * Route handler parameter decorator. Extracts the entire `body` * object from the `req` object and populates the decorated @@ -453,7 +551,23 @@ export function Body(): ParameterDecorator; export function Body( ...pipes: (Type | PipeTransform)[] ): ParameterDecorator; - +/** + * Route handler parameter decorator. Extracts the entire `body` object + * property, or optionally a named property of the `body` object, from + * the `req` object and populates the decorated parameter with that value. + * + * For example: + * ```typescript + * async create(@Body('role') role: string) + * ``` + * + * @param options options to apply to the bound body parameter. + * + * @see [Request object](https://docs.nestjs.com/controllers#request-object) + * + * @publicApi + */ +export function Body(options: ParameterDecoratorOptions): ParameterDecorator; /** * Route handler parameter decorator. Extracts a single property from * the `body` object property of the `req` object and populates the decorated @@ -478,7 +592,6 @@ export function Body( property: string, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator; - /** * Route handler parameter decorator. Extracts the entire `body` object * property, or optionally a named property of the `body` object, from @@ -491,6 +604,30 @@ export function Body( * ``` * * @param property name of single property to extract from the `body` object + * @param options options to apply to the bound body parameter. + * + * @see [Request object](https://docs.nestjs.com/controllers#request-object) + * @see [Working with pipes](https://docs.nestjs.com/custom-decorators#working-with-pipes) + * + * @publicApi + */ +export function Body( + property: string, + options: ParameterDecoratorOptions, +): ParameterDecorator; +/** + * Route handler parameter decorator. Extracts the entire `body` object + * property, or optionally a named property of the `body` object, from + * the `req` object and populates the decorated parameter with that value. + * Also applies pipes to the bound body parameter. + * + * For example: + * ```typescript + * async create(@Body('role', new ValidationPipe()) role: string) + * ``` + * + * @param property name of single property to extract from the `body` object + * @param optionsOrPipe options to apply to the bound body parameter. * @param pipes one or more pipes - either instances or classes - to apply to * the bound body parameter. * @@ -500,13 +637,44 @@ export function Body( * @publicApi */ export function Body( - property?: string | (Type | PipeTransform), + property?: + | string + | (Type | PipeTransform) + | ParameterDecoratorOptions, + optionsOrPipe?: + | ParameterDecoratorOptions + | Type + | PipeTransform, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator { - return createPipesRouteParamDecorator(RouteParamtypes.BODY)( - property, - ...pipes, - ); + const isPropertyOptions = + property && + typeof property === 'object' && + !('transform' in property) && + ('schema' in property || 'pipes' in property); + + if (isPropertyOptions) { + return createPipesRouteParamDecorator(RouteParamtypes.BODY)({ + pipes: property.pipes, + schema: property.schema, + }); + } + + const isOptions = + optionsOrPipe && + typeof optionsOrPipe === 'object' && + ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe); + const actualPipes = isOptions + ? optionsOrPipe.pipes + : ([optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]); + return createPipesRouteParamDecorator(RouteParamtypes.BODY)({ + data: property, + pipes: actualPipes, + schema: isOptions ? optionsOrPipe.schema : undefined, + }); } /** @@ -551,6 +719,26 @@ export function RawBody( )[] ): ParameterDecorator; +/** + * Route handler parameter decorator. Extracts the `rawBody` Buffer + * property from the `req` object and populates the decorated parameter with that value. + * Also applies pipes to the bound rawBody parameter. + * + * For example: + * ```typescript + * async create(@RawBody({ schema: z.instanceof(Buffer) }) rawBody: Buffer) + * ``` + * + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @see [Request object](https://docs.nestjs.com/controllers#request-object) + * @see [Raw body](https://docs.nestjs.com/faq/raw-body) + * @see [Working with pipes](https://docs.nestjs.com/custom-decorators#working-with-pipes) + * + * @publicApi + */ +export function RawBody(options: ParameterDecoratorOptions): ParameterDecorator; + /** * Route handler parameter decorator. Extracts the `rawBody` Buffer * property from the `req` object and populates the decorated parameter with that value. @@ -561,6 +749,7 @@ export function RawBody( * async create(@RawBody(new ValidationPipe()) rawBody: Buffer) * ``` * + * @param optionsOrPipe one or more pipes to apply or options object * @param pipes one or more pipes - either instances or classes - to apply to * the bound body parameter. * @@ -571,15 +760,29 @@ export function RawBody( * @publicApi */ export function RawBody( + optionsOrPipe?: + | ParameterDecoratorOptions + | Type> + | PipeTransform, ...pipes: ( | Type> | PipeTransform )[] ): ParameterDecorator { - return createPipesRouteParamDecorator(RouteParamtypes.RAW_BODY)( - undefined, - ...pipes, - ); + const isOptions = + optionsOrPipe && + typeof optionsOrPipe === 'object' && + ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe); + const actualPipes = isOptions + ? optionsOrPipe.pipes + : ([optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]); + return createPipesRouteParamDecorator(RouteParamtypes.RAW_BODY)({ + pipes: actualPipes, + schema: isOptions ? optionsOrPipe.schema : undefined, + }); } /** @@ -662,6 +865,47 @@ export function Param( property: string, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator; +/** + * Route handler parameter decorator. Extracts the `params` + * property from the `req` object and populates the decorated + * parameter with the value of `params`. May also apply pipes to the bound + * parameter. + * + * For example, extracting a single param: + * ```typescript + * findOne(@Param('id', { schema: z.string().uuid() }) id: string) + * ``` + * @param property name of single property to extract from the `req` object + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @see [Request object](https://docs.nestjs.com/controllers#request-object) + * @see [Working with pipes](https://docs.nestjs.com/custom-decorators#working-with-pipes) + * + * @publicApi + */ +export function Param( + property: string, + options: ParameterDecoratorOptions, +): ParameterDecorator; +/** + * Route handler parameter decorator. Extracts the `params` + * property from the `req` object and populates the decorated + * parameter with the value of `params`. May also apply pipes to the bound + * parameter. + * + * For example: + * ```typescript + * findOne(@Param({ schema: z.object({ id: z.string().uuid() }) }) params) + * ``` + * + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @see [Request object](https://docs.nestjs.com/controllers#request-object) + * @see [Working with pipes](https://docs.nestjs.com/custom-decorators#working-with-pipes) + * + * @publicApi + */ +export function Param(options: ParameterDecoratorOptions): ParameterDecorator; /** * Route handler parameter decorator. Extracts the `params` * property from the `req` object and populates the decorated @@ -678,6 +922,7 @@ export function Param( * findOne(@Param('id') id: string) * ``` * @param property name of single property to extract from the `req` object + * @param optionsOrPipe one or more pipes to apply to the bound parameter or options object * @param pipes one or more pipes - either instances or classes - to apply to * the bound parameter. * @@ -687,13 +932,44 @@ export function Param( * @publicApi */ export function Param( - property?: string | (Type | PipeTransform), + property?: + | string + | (Type | PipeTransform) + | ParameterDecoratorOptions, + optionsOrPipe?: + | ParameterDecoratorOptions + | Type + | PipeTransform, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator { - return createPipesRouteParamDecorator(RouteParamtypes.PARAM)( - property, - ...pipes, - ); + const isPropertyOptions = + property && + typeof property === 'object' && + !('transform' in property) && + ('schema' in property || 'pipes' in property); + + if (isPropertyOptions) { + return createPipesRouteParamDecorator(RouteParamtypes.PARAM)({ + pipes: property.pipes, + schema: property.schema, + }); + } + + const isOptions = + optionsOrPipe && + typeof optionsOrPipe === 'object' && + ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe); + const actualPipes = isOptions + ? optionsOrPipe.pipes + : ([optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]); + return createPipesRouteParamDecorator(RouteParamtypes.PARAM)({ + data: property, + pipes: actualPipes, + schema: isOptions ? optionsOrPipe.schema : undefined, + }); } /** diff --git a/packages/common/decorators/http/sse.decorator.ts b/packages/common/decorators/http/sse.decorator.ts index 5dae6aef7a8..302b7905290 100644 --- a/packages/common/decorators/http/sse.decorator.ts +++ b/packages/common/decorators/http/sse.decorator.ts @@ -1,5 +1,9 @@ -import { METHOD_METADATA, PATH_METADATA, SSE_METADATA } from '../../constants'; -import { RequestMethod } from '../../enums/request-method.enum'; +import { + METHOD_METADATA, + PATH_METADATA, + SSE_METADATA, +} from '../../constants.js'; +import { RequestMethod } from '../../enums/request-method.enum.js'; /** * Declares this route as a Server-Sent-Events endpoint diff --git a/packages/common/decorators/index.ts b/packages/common/decorators/index.ts index 20ab3a19c41..44fa86f967e 100644 --- a/packages/common/decorators/index.ts +++ b/packages/common/decorators/index.ts @@ -1,3 +1,3 @@ -export * from './core'; -export * from './modules'; -export * from './http'; +export * from './core/index.js'; +export * from './modules/index.js'; +export * from './http/index.js'; diff --git a/packages/common/decorators/modules/global.decorator.ts b/packages/common/decorators/modules/global.decorator.ts index 7b9c5ef2c9e..c9549056107 100644 --- a/packages/common/decorators/modules/global.decorator.ts +++ b/packages/common/decorators/modules/global.decorator.ts @@ -1,4 +1,4 @@ -import { GLOBAL_MODULE_METADATA } from '../../constants'; +import { GLOBAL_MODULE_METADATA } from '../../constants.js'; /** * Decorator that makes a module global-scoped. diff --git a/packages/common/decorators/modules/index.ts b/packages/common/decorators/modules/index.ts index e1994f74e79..1997b8fe963 100644 --- a/packages/common/decorators/modules/index.ts +++ b/packages/common/decorators/modules/index.ts @@ -1,2 +1,2 @@ -export * from './global.decorator'; -export * from './module.decorator'; +export * from './global.decorator.js'; +export * from './module.decorator.js'; diff --git a/packages/common/decorators/modules/module.decorator.ts b/packages/common/decorators/modules/module.decorator.ts index 9ab617a6435..5bef01365f2 100644 --- a/packages/common/decorators/modules/module.decorator.ts +++ b/packages/common/decorators/modules/module.decorator.ts @@ -1,5 +1,5 @@ -import { ModuleMetadata } from '../../interfaces/modules/module-metadata.interface'; -import { validateModuleKeys } from '../../utils/validate-module-keys.util'; +import { ModuleMetadata } from '../../interfaces/modules/module-metadata.interface.js'; +import { validateModuleKeys } from '../../utils/validate-module-keys.util.js'; /** * Decorator that marks a class as a [module](https://docs.nestjs.com/modules). diff --git a/packages/common/enums/index.ts b/packages/common/enums/index.ts index 13ee987c228..01bac419e7c 100644 --- a/packages/common/enums/index.ts +++ b/packages/common/enums/index.ts @@ -1,4 +1,4 @@ -export * from './request-method.enum'; -export * from './http-status.enum'; -export * from './shutdown-signal.enum'; -export * from './version-type.enum'; +export * from './request-method.enum.js'; +export * from './http-status.enum.js'; +export * from './shutdown-signal.enum.js'; +export * from './version-type.enum.js'; diff --git a/packages/common/exceptions/bad-gateway.exception.ts b/packages/common/exceptions/bad-gateway.exception.ts index 5bb9cd156a3..c9f3b33610b 100644 --- a/packages/common/exceptions/bad-gateway.exception.ts +++ b/packages/common/exceptions/bad-gateway.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Bad Gateway* type errors. @@ -37,13 +37,13 @@ export class BadGatewayException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Bad Gateway', ) { - const { description, httpExceptionOptions } = + const { description = 'Bad Gateway', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.BAD_GATEWAY, ), HttpStatus.BAD_GATEWAY, diff --git a/packages/common/exceptions/bad-request.exception.ts b/packages/common/exceptions/bad-request.exception.ts index 33ad6652b65..1bc580fbdfb 100644 --- a/packages/common/exceptions/bad-request.exception.ts +++ b/packages/common/exceptions/bad-request.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Bad Request* type errors. @@ -37,13 +37,13 @@ export class BadRequestException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Bad Request', ) { - const { description, httpExceptionOptions } = + const { description = 'Bad Request', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.BAD_REQUEST, ), HttpStatus.BAD_REQUEST, diff --git a/packages/common/exceptions/conflict.exception.ts b/packages/common/exceptions/conflict.exception.ts index 27bdda8aa07..18809838b5f 100644 --- a/packages/common/exceptions/conflict.exception.ts +++ b/packages/common/exceptions/conflict.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Conflict* type errors. @@ -37,15 +37,11 @@ export class ConflictException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Conflict', ) { - const { description, httpExceptionOptions } = + const { description = 'Conflict', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( - HttpException.createBody( - objectOrError, - description!, - HttpStatus.CONFLICT, - ), + HttpException.createBody(objectOrError, description, HttpStatus.CONFLICT), HttpStatus.CONFLICT, httpExceptionOptions, ); diff --git a/packages/common/exceptions/forbidden.exception.ts b/packages/common/exceptions/forbidden.exception.ts index f04b86bcaa0..00e89e679ce 100644 --- a/packages/common/exceptions/forbidden.exception.ts +++ b/packages/common/exceptions/forbidden.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Forbidden* type errors. @@ -37,13 +37,13 @@ export class ForbiddenException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Forbidden', ) { - const { description, httpExceptionOptions } = + const { description = 'Forbidden', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.FORBIDDEN, ), HttpStatus.FORBIDDEN, diff --git a/packages/common/exceptions/gateway-timeout.exception.ts b/packages/common/exceptions/gateway-timeout.exception.ts index 49dee49381f..e7ef9958d39 100644 --- a/packages/common/exceptions/gateway-timeout.exception.ts +++ b/packages/common/exceptions/gateway-timeout.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Gateway Timeout* type errors. @@ -37,13 +37,13 @@ export class GatewayTimeoutException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Gateway Timeout', ) { - const { description, httpExceptionOptions } = + const { description = 'Gateway Timeout', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.GATEWAY_TIMEOUT, ), HttpStatus.GATEWAY_TIMEOUT, diff --git a/packages/common/exceptions/gone.exception.ts b/packages/common/exceptions/gone.exception.ts index b1fc0f65b52..cadf3a0702b 100644 --- a/packages/common/exceptions/gone.exception.ts +++ b/packages/common/exceptions/gone.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Gone* type errors. @@ -37,11 +37,11 @@ export class GoneException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Gone', ) { - const { description, httpExceptionOptions } = + const { description = 'Gone', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( - HttpException.createBody(objectOrError, description!, HttpStatus.GONE), + HttpException.createBody(objectOrError, description, HttpStatus.GONE), HttpStatus.GONE, httpExceptionOptions, ); diff --git a/packages/common/exceptions/http-version-not-supported.exception.ts b/packages/common/exceptions/http-version-not-supported.exception.ts index 23283d343e2..8c7451ef21f 100644 --- a/packages/common/exceptions/http-version-not-supported.exception.ts +++ b/packages/common/exceptions/http-version-not-supported.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Http Version Not Supported* type errors. @@ -39,13 +39,13 @@ export class HttpVersionNotSupportedException extends HttpException { | string | HttpExceptionOptions = 'HTTP Version Not Supported', ) { - const { description, httpExceptionOptions } = + const { description = 'HTTP Version Not Supported', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.HTTP_VERSION_NOT_SUPPORTED, ), HttpStatus.HTTP_VERSION_NOT_SUPPORTED, diff --git a/packages/common/exceptions/http.exception.ts b/packages/common/exceptions/http.exception.ts index 8b21b9a392f..1d4782103a0 100644 --- a/packages/common/exceptions/http.exception.ts +++ b/packages/common/exceptions/http.exception.ts @@ -1,14 +1,15 @@ import { HttpExceptionBody, HttpExceptionBodyMessage, -} from '../interfaces/http/http-exception-body.interface'; -import { isNumber, isObject, isString } from '../utils/shared.utils'; -import { IntrinsicException } from './intrinsic.exception'; +} from '../interfaces/http/http-exception-body.interface.js'; +import { isNumber, isObject, isString } from '../utils/shared.utils.js'; +import { IntrinsicException } from './intrinsic.exception.js'; export interface HttpExceptionOptions { /** original cause of the error */ cause?: unknown; description?: string; + errorCode?: string; } export interface DescriptionAndOptions { @@ -30,6 +31,7 @@ export class HttpException extends IntrinsicException { * It is used when catching and re-throwing an error with a more-specific or useful error message in order to still have access to the original error. */ public cause: unknown; + public errorCode?: string; /** * Instantiate a plain HTTP Exception. @@ -73,6 +75,7 @@ export class HttpException extends IntrinsicException { this.initMessage(); this.initName(); this.initCause(); + this.initErrorCode(); } /** @@ -88,6 +91,12 @@ export class HttpException extends IntrinsicException { } } + public initErrorCode(): void { + if (this.options?.errorCode) { + this.errorCode = this.options.errorCode; + } + } + public initMessage() { if (isString(this.response)) { this.message = this.response; @@ -122,6 +131,12 @@ export class HttpException extends IntrinsicException { error: string, statusCode: number, ): HttpExceptionBody; + public static createBody( + message: HttpExceptionBodyMessage, + error: string, + statusCode: number, + errorCode?: string, + ): HttpExceptionBody; public static createBody>( custom: Body, ): Body; @@ -129,20 +144,29 @@ export class HttpException extends IntrinsicException { arg0: null | HttpExceptionBodyMessage | Body, arg1?: HttpExceptionBodyMessage | string, statusCode?: number, + errorCode?: string, ): HttpExceptionBody | Body { if (!arg0) { - return { + const body: HttpExceptionBody = { message: arg1!, statusCode: statusCode!, }; + if (errorCode) { + body.errorCode = errorCode; + } + return body; } if (isString(arg0) || Array.isArray(arg0) || isNumber(arg0)) { - return { + const body: HttpExceptionBody = { message: arg0, error: arg1 as string, statusCode: statusCode!, }; + if (errorCode) { + body.errorCode = errorCode; + } + return body; } return arg0; diff --git a/packages/common/exceptions/im-a-teapot.exception.ts b/packages/common/exceptions/im-a-teapot.exception.ts index 209e32c4380..ab181289de7 100644 --- a/packages/common/exceptions/im-a-teapot.exception.ts +++ b/packages/common/exceptions/im-a-teapot.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *ImATeapotException* type errors. @@ -40,13 +40,13 @@ export class ImATeapotException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = `I'm a teapot`, ) { - const { description, httpExceptionOptions } = + const { description = `I'm a teapot`, httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.I_AM_A_TEAPOT, ), HttpStatus.I_AM_A_TEAPOT, diff --git a/packages/common/exceptions/index.ts b/packages/common/exceptions/index.ts index 554218336df..b46622e7a89 100644 --- a/packages/common/exceptions/index.ts +++ b/packages/common/exceptions/index.ts @@ -1,23 +1,23 @@ -export * from './bad-gateway.exception'; -export * from './bad-request.exception'; -export * from './conflict.exception'; -export * from './forbidden.exception'; -export * from './gateway-timeout.exception'; -export * from './gone.exception'; -export * from './http-version-not-supported.exception'; -export * from './http.exception'; -export * from './im-a-teapot.exception'; -export * from './internal-server-error.exception'; -export * from './intrinsic.exception'; -export * from './method-not-allowed.exception'; -export * from './misdirected.exception'; -export * from './not-acceptable.exception'; -export * from './not-found.exception'; -export * from './not-implemented.exception'; -export * from './payload-too-large.exception'; -export * from './precondition-failed.exception'; -export * from './request-timeout.exception'; -export * from './service-unavailable.exception'; -export * from './unauthorized.exception'; -export * from './unprocessable-entity.exception'; -export * from './unsupported-media-type.exception'; +export * from './bad-gateway.exception.js'; +export * from './bad-request.exception.js'; +export * from './conflict.exception.js'; +export * from './forbidden.exception.js'; +export * from './gateway-timeout.exception.js'; +export * from './gone.exception.js'; +export * from './http-version-not-supported.exception.js'; +export * from './http.exception.js'; +export * from './im-a-teapot.exception.js'; +export * from './internal-server-error.exception.js'; +export * from './intrinsic.exception.js'; +export * from './method-not-allowed.exception.js'; +export * from './misdirected.exception.js'; +export * from './not-acceptable.exception.js'; +export * from './not-found.exception.js'; +export * from './not-implemented.exception.js'; +export * from './payload-too-large.exception.js'; +export * from './precondition-failed.exception.js'; +export * from './request-timeout.exception.js'; +export * from './service-unavailable.exception.js'; +export * from './unauthorized.exception.js'; +export * from './unprocessable-entity.exception.js'; +export * from './unsupported-media-type.exception.js'; diff --git a/packages/common/exceptions/internal-server-error.exception.ts b/packages/common/exceptions/internal-server-error.exception.ts index f8955b86afb..cfe002ed35e 100644 --- a/packages/common/exceptions/internal-server-error.exception.ts +++ b/packages/common/exceptions/internal-server-error.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Internal Server Error* type errors. @@ -39,13 +39,13 @@ export class InternalServerErrorException extends HttpException { | string | HttpExceptionOptions = 'Internal Server Error', ) { - const { description, httpExceptionOptions } = + const { description = 'Internal Server Error', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.INTERNAL_SERVER_ERROR, ), HttpStatus.INTERNAL_SERVER_ERROR, diff --git a/packages/common/exceptions/method-not-allowed.exception.ts b/packages/common/exceptions/method-not-allowed.exception.ts index e6db6198cf5..d16cb37e00c 100644 --- a/packages/common/exceptions/method-not-allowed.exception.ts +++ b/packages/common/exceptions/method-not-allowed.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Method Not Allowed* type errors. @@ -37,13 +37,13 @@ export class MethodNotAllowedException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Method Not Allowed', ) { - const { description, httpExceptionOptions } = + const { description = 'Method Not Allowed', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.METHOD_NOT_ALLOWED, ), HttpStatus.METHOD_NOT_ALLOWED, diff --git a/packages/common/exceptions/misdirected.exception.ts b/packages/common/exceptions/misdirected.exception.ts index 4ff86459f62..c95515c263b 100644 --- a/packages/common/exceptions/misdirected.exception.ts +++ b/packages/common/exceptions/misdirected.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Misdirected* type errors. @@ -37,13 +37,13 @@ export class MisdirectedException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Misdirected', ) { - const { description, httpExceptionOptions } = + const { description = 'Misdirected', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.MISDIRECTED, ), HttpStatus.MISDIRECTED, diff --git a/packages/common/exceptions/not-acceptable.exception.ts b/packages/common/exceptions/not-acceptable.exception.ts index 8b3cf66a028..e2883bd8a1f 100644 --- a/packages/common/exceptions/not-acceptable.exception.ts +++ b/packages/common/exceptions/not-acceptable.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Not Acceptable* type errors. @@ -37,13 +37,13 @@ export class NotAcceptableException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Not Acceptable', ) { - const { description, httpExceptionOptions } = + const { description = 'Not Acceptable', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.NOT_ACCEPTABLE, ), HttpStatus.NOT_ACCEPTABLE, diff --git a/packages/common/exceptions/not-found.exception.ts b/packages/common/exceptions/not-found.exception.ts index 1213e8dca8b..96c0bf87549 100644 --- a/packages/common/exceptions/not-found.exception.ts +++ b/packages/common/exceptions/not-found.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Not Found* type errors. @@ -37,13 +37,13 @@ export class NotFoundException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Not Found', ) { - const { description, httpExceptionOptions } = + const { description = 'Not Found', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.NOT_FOUND, ), HttpStatus.NOT_FOUND, diff --git a/packages/common/exceptions/not-implemented.exception.ts b/packages/common/exceptions/not-implemented.exception.ts index 206a87333a7..57cce136f50 100644 --- a/packages/common/exceptions/not-implemented.exception.ts +++ b/packages/common/exceptions/not-implemented.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Not Implemented* type errors. @@ -37,13 +37,13 @@ export class NotImplementedException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Not Implemented', ) { - const { description, httpExceptionOptions } = + const { description = 'Not Implemented', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.NOT_IMPLEMENTED, ), HttpStatus.NOT_IMPLEMENTED, diff --git a/packages/common/exceptions/payload-too-large.exception.ts b/packages/common/exceptions/payload-too-large.exception.ts index 4e841a35911..34983ab58ea 100644 --- a/packages/common/exceptions/payload-too-large.exception.ts +++ b/packages/common/exceptions/payload-too-large.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Payload Too Large* type errors. @@ -37,13 +37,13 @@ export class PayloadTooLargeException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Payload Too Large', ) { - const { description, httpExceptionOptions } = + const { description = 'Payload Too Large', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.PAYLOAD_TOO_LARGE, ), HttpStatus.PAYLOAD_TOO_LARGE, diff --git a/packages/common/exceptions/precondition-failed.exception.ts b/packages/common/exceptions/precondition-failed.exception.ts index 68c3433c7f7..ef172f9c57a 100644 --- a/packages/common/exceptions/precondition-failed.exception.ts +++ b/packages/common/exceptions/precondition-failed.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Precondition Failed* type errors. @@ -37,13 +37,13 @@ export class PreconditionFailedException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Precondition Failed', ) { - const { description, httpExceptionOptions } = + const { description = 'Precondition Failed', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.PRECONDITION_FAILED, ), HttpStatus.PRECONDITION_FAILED, diff --git a/packages/common/exceptions/request-timeout.exception.ts b/packages/common/exceptions/request-timeout.exception.ts index f4f6d795566..a34c546800a 100644 --- a/packages/common/exceptions/request-timeout.exception.ts +++ b/packages/common/exceptions/request-timeout.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Request Timeout* type errors. @@ -37,13 +37,13 @@ export class RequestTimeoutException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Request Timeout', ) { - const { description, httpExceptionOptions } = + const { description = 'Request Timeout', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.REQUEST_TIMEOUT, ), HttpStatus.REQUEST_TIMEOUT, diff --git a/packages/common/exceptions/service-unavailable.exception.ts b/packages/common/exceptions/service-unavailable.exception.ts index 0afca4bdd5f..849a8c47682 100644 --- a/packages/common/exceptions/service-unavailable.exception.ts +++ b/packages/common/exceptions/service-unavailable.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Service Unavailable* type errors. @@ -37,13 +37,13 @@ export class ServiceUnavailableException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Service Unavailable', ) { - const { description, httpExceptionOptions } = + const { description = 'Service Unavailable', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.SERVICE_UNAVAILABLE, ), HttpStatus.SERVICE_UNAVAILABLE, diff --git a/packages/common/exceptions/unauthorized.exception.ts b/packages/common/exceptions/unauthorized.exception.ts index c5be32253ff..794ad781776 100644 --- a/packages/common/exceptions/unauthorized.exception.ts +++ b/packages/common/exceptions/unauthorized.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Unauthorized* type errors. @@ -37,13 +37,13 @@ export class UnauthorizedException extends HttpException { objectOrError?: any, descriptionOrOptions: string | HttpExceptionOptions = 'Unauthorized', ) { - const { description, httpExceptionOptions } = + const { description = 'Unauthorized', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.UNAUTHORIZED, ), HttpStatus.UNAUTHORIZED, diff --git a/packages/common/exceptions/unprocessable-entity.exception.ts b/packages/common/exceptions/unprocessable-entity.exception.ts index d6f99aa253f..a588b0d2ae3 100644 --- a/packages/common/exceptions/unprocessable-entity.exception.ts +++ b/packages/common/exceptions/unprocessable-entity.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Unprocessable Entity* type errors. @@ -39,13 +39,13 @@ export class UnprocessableEntityException extends HttpException { | string | HttpExceptionOptions = 'Unprocessable Entity', ) { - const { description, httpExceptionOptions } = + const { description = 'Unprocessable Entity', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.UNPROCESSABLE_ENTITY, ), HttpStatus.UNPROCESSABLE_ENTITY, diff --git a/packages/common/exceptions/unsupported-media-type.exception.ts b/packages/common/exceptions/unsupported-media-type.exception.ts index 9d8af94af26..92bce97ad30 100644 --- a/packages/common/exceptions/unsupported-media-type.exception.ts +++ b/packages/common/exceptions/unsupported-media-type.exception.ts @@ -1,5 +1,5 @@ -import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException, HttpExceptionOptions } from './http.exception'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { HttpException, HttpExceptionOptions } from './http.exception.js'; /** * Defines an HTTP exception for *Unsupported Media Type* type errors. @@ -39,13 +39,13 @@ export class UnsupportedMediaTypeException extends HttpException { | string | HttpExceptionOptions = 'Unsupported Media Type', ) { - const { description, httpExceptionOptions } = + const { description = 'Unsupported Media Type', httpExceptionOptions } = HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); super( HttpException.createBody( objectOrError, - description!, + description, HttpStatus.UNSUPPORTED_MEDIA_TYPE, ), HttpStatus.UNSUPPORTED_MEDIA_TYPE, diff --git a/packages/common/file-stream/index.ts b/packages/common/file-stream/index.ts index 75a39abefa3..7074127b3ef 100644 --- a/packages/common/file-stream/index.ts +++ b/packages/common/file-stream/index.ts @@ -1 +1 @@ -export * from './streamable-file'; +export * from './streamable-file.js'; diff --git a/packages/common/file-stream/interfaces/index.ts b/packages/common/file-stream/interfaces/index.ts index 9be413e21b4..e5e47d99c38 100644 --- a/packages/common/file-stream/interfaces/index.ts +++ b/packages/common/file-stream/interfaces/index.ts @@ -1,2 +1,2 @@ -export * from './streamable-options.interface'; -export * from './streamable-handler-response.interface'; +export * from './streamable-options.interface.js'; +export * from './streamable-handler-response.interface.js'; diff --git a/packages/common/file-stream/streamable-file.ts b/packages/common/file-stream/streamable-file.ts index 6323ca0656c..f77e84514a9 100644 --- a/packages/common/file-stream/streamable-file.ts +++ b/packages/common/file-stream/streamable-file.ts @@ -1,9 +1,12 @@ import { Readable } from 'stream'; import { types } from 'util'; -import { HttpStatus } from '../enums'; -import { Logger } from '../services'; -import { isFunction } from '../utils/shared.utils'; -import { StreamableFileOptions, StreamableHandlerResponse } from './interfaces'; +import { HttpStatus } from '../enums/index.js'; +import { Logger } from '../services/index.js'; +import { isFunction } from '../utils/shared.utils.js'; +import { + StreamableFileOptions, + StreamableHandlerResponse, +} from './interfaces/index.js'; /** * @see [Streaming files](https://docs.nestjs.com/techniques/streaming-files) diff --git a/packages/common/index.ts b/packages/common/index.ts index 1ccee53e75c..73023feb198 100644 --- a/packages/common/index.ts +++ b/packages/common/index.ts @@ -6,10 +6,10 @@ */ import 'reflect-metadata'; -export * from './decorators'; -export * from './enums'; -export * from './exceptions'; -export * from './file-stream'; +export * from './decorators/index.js'; +export * from './enums/index.js'; +export * from './exceptions/index.js'; +export * from './file-stream/index.js'; export { Abstract, ArgumentMetadata, @@ -41,6 +41,7 @@ export { NestHybridApplicationOptions, NestInterceptor, NestMiddleware, + PreRequestHook, NestModule, OnApplicationBootstrap, OnApplicationShutdown, @@ -62,9 +63,9 @@ export { WebSocketAdapter, WsExceptionFilter, WsMessageHandler, -} from './interfaces'; -export * from './module-utils'; -export * from './pipes'; -export * from './serializer'; -export * from './services'; -export * from './utils'; +} from './interfaces/index.js'; +export * from './module-utils/index.js'; +export * from './pipes/index.js'; +export * from './serializer/index.js'; +export * from './services/index.js'; +export * from './utils/index.js'; diff --git a/packages/common/interfaces/controllers/index.ts b/packages/common/interfaces/controllers/index.ts index 4bd60924b00..a51741c4b2b 100644 --- a/packages/common/interfaces/controllers/index.ts +++ b/packages/common/interfaces/controllers/index.ts @@ -1,2 +1,2 @@ -export * from './controller-metadata.interface'; -export * from './controller.interface'; +export * from './controller-metadata.interface.js'; +export * from './controller.interface.js'; diff --git a/packages/common/interfaces/exceptions/exception-filter-metadata.interface.ts b/packages/common/interfaces/exceptions/exception-filter-metadata.interface.ts index 364ef37897e..c873d707ce8 100644 --- a/packages/common/interfaces/exceptions/exception-filter-metadata.interface.ts +++ b/packages/common/interfaces/exceptions/exception-filter-metadata.interface.ts @@ -1,5 +1,5 @@ -import { ExceptionFilter } from './exception-filter.interface'; -import { Type } from '../type.interface'; +import { ExceptionFilter } from './exception-filter.interface.js'; +import { Type } from '../type.interface.js'; export interface ExceptionFilterMetadata { func: ExceptionFilter['catch']; diff --git a/packages/common/interfaces/exceptions/exception-filter.interface.ts b/packages/common/interfaces/exceptions/exception-filter.interface.ts index 96638a75989..08d01727855 100644 --- a/packages/common/interfaces/exceptions/exception-filter.interface.ts +++ b/packages/common/interfaces/exceptions/exception-filter.interface.ts @@ -1,4 +1,4 @@ -import { ArgumentsHost } from '../features/arguments-host.interface'; +import { ArgumentsHost } from '../features/arguments-host.interface.js'; /** * Interface describing implementation of an exception filter. diff --git a/packages/common/interfaces/exceptions/index.ts b/packages/common/interfaces/exceptions/index.ts index f35002c2e21..9d0fedd3df8 100644 --- a/packages/common/interfaces/exceptions/index.ts +++ b/packages/common/interfaces/exceptions/index.ts @@ -1,5 +1,5 @@ -export * from './exception-filter-metadata.interface'; -export * from './exception-filter.interface'; -export * from './rpc-exception-filter-metadata.interface'; -export * from './rpc-exception-filter.interface'; -export * from './ws-exception-filter.interface'; +export * from './exception-filter-metadata.interface.js'; +export * from './exception-filter.interface.js'; +export * from './rpc-exception-filter-metadata.interface.js'; +export * from './rpc-exception-filter.interface.js'; +export * from './ws-exception-filter.interface.js'; diff --git a/packages/common/interfaces/exceptions/rpc-exception-filter-metadata.interface.ts b/packages/common/interfaces/exceptions/rpc-exception-filter-metadata.interface.ts index 9639705fda1..19073ef12d7 100644 --- a/packages/common/interfaces/exceptions/rpc-exception-filter-metadata.interface.ts +++ b/packages/common/interfaces/exceptions/rpc-exception-filter-metadata.interface.ts @@ -1,5 +1,5 @@ -import { RpcExceptionFilter } from './rpc-exception-filter.interface'; -import { Type } from '../type.interface'; +import { RpcExceptionFilter } from './rpc-exception-filter.interface.js'; +import { Type } from '../type.interface.js'; export interface RpcExceptionFilterMetadata { func: RpcExceptionFilter['catch']; diff --git a/packages/common/interfaces/exceptions/rpc-exception-filter.interface.ts b/packages/common/interfaces/exceptions/rpc-exception-filter.interface.ts index 75e09caf89c..0982091aae7 100644 --- a/packages/common/interfaces/exceptions/rpc-exception-filter.interface.ts +++ b/packages/common/interfaces/exceptions/rpc-exception-filter.interface.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs'; -import { ArgumentsHost } from '../features/arguments-host.interface'; +import { ArgumentsHost } from '../features/arguments-host.interface.js'; /** * Interface describing implementation of an RPC exception filter. diff --git a/packages/common/interfaces/exceptions/ws-exception-filter.interface.ts b/packages/common/interfaces/exceptions/ws-exception-filter.interface.ts index 179f60a2f8a..ee5fdfd8518 100644 --- a/packages/common/interfaces/exceptions/ws-exception-filter.interface.ts +++ b/packages/common/interfaces/exceptions/ws-exception-filter.interface.ts @@ -1,4 +1,4 @@ -import { ArgumentsHost } from '../features/arguments-host.interface'; +import { ArgumentsHost } from '../features/arguments-host.interface.js'; /** * Interface describing implementation of a Web Sockets exception filter. diff --git a/packages/common/interfaces/external/transformer-package.interface.ts b/packages/common/interfaces/external/transformer-package.interface.ts index f13c5fa2e10..1d2a01a5ee0 100644 --- a/packages/common/interfaces/external/transformer-package.interface.ts +++ b/packages/common/interfaces/external/transformer-package.interface.ts @@ -1,5 +1,5 @@ -import { Type } from '../type.interface'; -import { ClassTransformOptions } from './class-transform-options.interface'; +import { Type } from '../type.interface.js'; +import { ClassTransformOptions } from './class-transform-options.interface.js'; export interface TransformerPackage { plainToInstance( diff --git a/packages/common/interfaces/external/validator-package.interface.ts b/packages/common/interfaces/external/validator-package.interface.ts index 5b5e259cfbf..a1923fd5059 100644 --- a/packages/common/interfaces/external/validator-package.interface.ts +++ b/packages/common/interfaces/external/validator-package.interface.ts @@ -1,5 +1,5 @@ -import { ValidationError } from './validation-error.interface'; -import { ValidatorOptions } from './validator-options.interface'; +import { ValidationError } from './validation-error.interface.js'; +import { ValidatorOptions } from './validator-options.interface.js'; export interface ValidatorPackage { validate( diff --git a/packages/common/interfaces/features/can-activate.interface.ts b/packages/common/interfaces/features/can-activate.interface.ts index 29eb5e2ed04..1256483fb67 100644 --- a/packages/common/interfaces/features/can-activate.interface.ts +++ b/packages/common/interfaces/features/can-activate.interface.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs'; -import { ExecutionContext } from './execution-context.interface'; +import { ExecutionContext } from './execution-context.interface.js'; /** * Interface defining the `canActivate()` function that must be implemented diff --git a/packages/common/interfaces/features/custom-route-param-factory.interface.ts b/packages/common/interfaces/features/custom-route-param-factory.interface.ts index c63b4b85739..7ba4be1484d 100644 --- a/packages/common/interfaces/features/custom-route-param-factory.interface.ts +++ b/packages/common/interfaces/features/custom-route-param-factory.interface.ts @@ -1,4 +1,4 @@ -import { ExecutionContext } from './execution-context.interface'; +import { ExecutionContext } from './execution-context.interface.js'; /** * @publicApi diff --git a/packages/common/interfaces/features/execution-context.interface.ts b/packages/common/interfaces/features/execution-context.interface.ts index 68e95accbb9..30dfeb22af8 100644 --- a/packages/common/interfaces/features/execution-context.interface.ts +++ b/packages/common/interfaces/features/execution-context.interface.ts @@ -1,5 +1,5 @@ -import { Type } from '../index'; -import { ArgumentsHost } from './arguments-host.interface'; +import { Type } from '../index.js'; +import { ArgumentsHost } from './arguments-host.interface.js'; /** * Interface describing details about the current request pipeline. diff --git a/packages/common/interfaces/features/nest-interceptor.interface.ts b/packages/common/interfaces/features/nest-interceptor.interface.ts index ec4ad808918..7ebaf576ca0 100644 --- a/packages/common/interfaces/features/nest-interceptor.interface.ts +++ b/packages/common/interfaces/features/nest-interceptor.interface.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs'; -import { ExecutionContext } from './execution-context.interface'; +import { ExecutionContext } from './execution-context.interface.js'; /** * Interface providing access to the response stream. diff --git a/packages/common/interfaces/features/pipe-transform.interface.ts b/packages/common/interfaces/features/pipe-transform.interface.ts index 60ce3b66345..bb219a577c1 100644 --- a/packages/common/interfaces/features/pipe-transform.interface.ts +++ b/packages/common/interfaces/features/pipe-transform.interface.ts @@ -1,5 +1,6 @@ -import { Type } from '../type.interface'; -import { Paramtype } from './paramtype.interface'; +import { Type } from '../type.interface.js'; +import { Paramtype } from './paramtype.interface.js'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; export type Transform = (value: T, metadata: ArgumentMetadata) => any; @@ -10,7 +11,7 @@ export type Transform = (value: T, metadata: ArgumentMetadata) => any; * * @publicApi */ -export interface ArgumentMetadata { +export interface ArgumentMetadata { /** * Indicates whether argument is a body, query, param, or custom parameter */ @@ -19,12 +20,17 @@ export interface ArgumentMetadata { * Underlying base type (e.g., `String`) of the parameter, based on the type * definition in the route handler. */ - readonly metatype?: Type | undefined; + readonly metatype?: Type | undefined; /** * String passed as an argument to the decorator. * Example: `@Body('userId')` would yield `userId` */ readonly data?: string | undefined; + /** + * A standard schema object. + * Can be used to validate the parameter's value against the schema, or to generate API. + */ + readonly schema?: StandardSchemaV1; } /** diff --git a/packages/common/interfaces/global-prefix-options.interface.ts b/packages/common/interfaces/global-prefix-options.interface.ts index e8f657683de..0da785c587e 100644 --- a/packages/common/interfaces/global-prefix-options.interface.ts +++ b/packages/common/interfaces/global-prefix-options.interface.ts @@ -1,4 +1,4 @@ -import { RouteInfo } from './middleware'; +import { RouteInfo } from './middleware/index.js'; /** * @publicApi diff --git a/packages/common/interfaces/hooks/index.ts b/packages/common/interfaces/hooks/index.ts index 01aad24975c..14c5bb96928 100644 --- a/packages/common/interfaces/hooks/index.ts +++ b/packages/common/interfaces/hooks/index.ts @@ -1,5 +1,5 @@ -export * from './before-application-shutdown.interface'; -export * from './on-application-bootstrap.interface'; -export * from './on-application-shutdown.interface'; -export * from './on-destroy.interface'; -export * from './on-init.interface'; +export * from './before-application-shutdown.interface.js'; +export * from './on-application-bootstrap.interface.js'; +export * from './on-application-shutdown.interface.js'; +export * from './on-destroy.interface.js'; +export * from './on-init.interface.js'; diff --git a/packages/common/interfaces/http/http-exception-body.interface.ts b/packages/common/interfaces/http/http-exception-body.interface.ts index b9f4732d622..9f12e4c549d 100644 --- a/packages/common/interfaces/http/http-exception-body.interface.ts +++ b/packages/common/interfaces/http/http-exception-body.interface.ts @@ -1,7 +1,8 @@ export type HttpExceptionBodyMessage = string | string[] | number; export interface HttpExceptionBody { + statusCode: number; message: HttpExceptionBodyMessage; error?: string; - statusCode: number; + errorCode?: string; } diff --git a/packages/common/interfaces/http/http-redirect-response.interface.ts b/packages/common/interfaces/http/http-redirect-response.interface.ts index da3261eba30..c775e9f2ad7 100644 --- a/packages/common/interfaces/http/http-redirect-response.interface.ts +++ b/packages/common/interfaces/http/http-redirect-response.interface.ts @@ -1,4 +1,4 @@ -import { HttpStatus } from '../../enums'; +import { HttpStatus } from '../../enums/index.js'; export interface HttpRedirectResponse { url: string; diff --git a/packages/common/interfaces/http/http-server.interface.ts b/packages/common/interfaces/http/http-server.interface.ts index 8ed861c85b2..66ee443d00f 100644 --- a/packages/common/interfaces/http/http-server.interface.ts +++ b/packages/common/interfaces/http/http-server.interface.ts @@ -1,6 +1,9 @@ -import { RequestMethod } from '../../enums'; -import { NestApplicationOptions } from '../../interfaces/nest-application-options.interface'; -import { VersionValue, VersioningOptions } from '../version-options.interface'; +import { RequestMethod } from '../../enums/index.js'; +import { NestApplicationOptions } from '../../interfaces/nest-application-options.interface.js'; +import { + VersionValue, + VersioningOptions, +} from '../version-options.interface.js'; export type ErrorHandler = ( error: any, @@ -91,6 +94,7 @@ export interface HttpServer< getHttpServer(): any; initHttpServer(options: NestApplicationOptions): void; close(): any; + beforeClose?(): any; getType(): string; init?(): Promise; applyVersionFilter( diff --git a/packages/common/interfaces/http/index.ts b/packages/common/interfaces/http/index.ts index ad8ebfc83fd..711b0ca840c 100644 --- a/packages/common/interfaces/http/index.ts +++ b/packages/common/interfaces/http/index.ts @@ -1,5 +1,5 @@ -export * from './http-exception-body.interface'; -export * from './http-redirect-response.interface'; -export * from './http-server.interface'; -export * from './message-event.interface'; -export * from './raw-body-request.interface'; +export * from './http-exception-body.interface.js'; +export * from './http-redirect-response.interface.js'; +export * from './http-server.interface.js'; +export * from './message-event.interface.js'; +export * from './raw-body-request.interface.js'; diff --git a/packages/common/interfaces/index.ts b/packages/common/interfaces/index.ts index 94ceff93cee..c3474f5adf0 100644 --- a/packages/common/interfaces/index.ts +++ b/packages/common/interfaces/index.ts @@ -1,30 +1,31 @@ -export * from './abstract.interface'; -export * from './controllers/controller-metadata.interface'; -export * from './controllers/controller.interface'; -export * from './exceptions/exception-filter.interface'; -export * from './exceptions/rpc-exception-filter.interface'; -export * from './exceptions/ws-exception-filter.interface'; -export * from './external/validation-error.interface'; -export * from './features/arguments-host.interface'; -export * from './features/can-activate.interface'; -export * from './features/custom-route-param-factory.interface'; -export * from './features/execution-context.interface'; -export * from './features/nest-interceptor.interface'; -export * from './features/paramtype.interface'; -export * from './features/pipe-transform.interface'; -export * from './global-prefix-options.interface'; -export * from './hooks'; -export * from './http'; -export * from './injectable.interface'; -export * from './microservices/nest-hybrid-application-options.interface'; -export * from './middleware'; -export * from './modules'; -export * from './nest-application-context.interface'; -export * from './nest-application-options.interface'; -export * from './nest-application.interface'; -export * from './nest-microservice.interface'; -export * from './scope-options.interface'; -export * from './shutdown-hooks-options.interface'; -export * from './type.interface'; -export * from './version-options.interface'; -export * from './websockets/web-socket-adapter.interface'; +export * from './abstract.interface.js'; +export * from './controllers/controller-metadata.interface.js'; +export * from './controllers/controller.interface.js'; +export * from './exceptions/exception-filter.interface.js'; +export * from './exceptions/rpc-exception-filter.interface.js'; +export * from './exceptions/ws-exception-filter.interface.js'; +export * from './external/validation-error.interface.js'; +export * from './features/arguments-host.interface.js'; +export * from './features/can-activate.interface.js'; +export * from './features/custom-route-param-factory.interface.js'; +export * from './features/execution-context.interface.js'; +export * from './features/nest-interceptor.interface.js'; +export * from './features/paramtype.interface.js'; +export * from './features/pipe-transform.interface.js'; +export * from './global-prefix-options.interface.js'; +export * from './hooks/index.js'; +export * from './http/index.js'; +export * from './injectable.interface.js'; +export * from './microservices/nest-hybrid-application-options.interface.js'; +export * from './microservices/pre-request-hook.interface.js'; +export * from './middleware/index.js'; +export * from './modules/index.js'; +export * from './nest-application-context.interface.js'; +export * from './nest-application-options.interface.js'; +export * from './nest-application.interface.js'; +export * from './nest-microservice.interface.js'; +export * from './scope-options.interface.js'; +export * from './shutdown-hooks-options.interface.js'; +export * from './type.interface.js'; +export * from './version-options.interface.js'; +export * from './websockets/web-socket-adapter.interface.js'; diff --git a/packages/common/interfaces/microservices/nest-microservice-options.interface.ts b/packages/common/interfaces/microservices/nest-microservice-options.interface.ts index 34f32c9f51f..684bcdf7964 100644 --- a/packages/common/interfaces/microservices/nest-microservice-options.interface.ts +++ b/packages/common/interfaces/microservices/nest-microservice-options.interface.ts @@ -1,4 +1,4 @@ -import { NestApplicationContextOptions } from '../nest-application-context-options.interface'; +import { NestApplicationContextOptions } from '../nest-application-context-options.interface.js'; /** * @publicApi diff --git a/packages/common/interfaces/microservices/pre-request-hook.interface.ts b/packages/common/interfaces/microservices/pre-request-hook.interface.ts new file mode 100644 index 00000000000..20e2122c0c7 --- /dev/null +++ b/packages/common/interfaces/microservices/pre-request-hook.interface.ts @@ -0,0 +1,26 @@ +import { Observable } from 'rxjs'; +import { ExecutionContext } from '../features/execution-context.interface.js'; + +/** + * Interface describing a global preRequest hook for microservices. + * + * Hooks are executed before guards, allowing setup of context (e.g. AsyncLocalStorage) + * that is available to all downstream enhancers. + * + * @example + * ```typescript + * const als = new AsyncLocalStorage(); + * app.registerPreRequestHook((context, next) => { + * als.enterWith({ correlationId: uuid() }); + * return next(); + * }); + * ``` + * + * @publicApi + */ +export interface PreRequestHook { + ( + context: ExecutionContext, + next: () => Observable, + ): Observable; +} diff --git a/packages/common/interfaces/middleware/index.ts b/packages/common/interfaces/middleware/index.ts index b23e220972a..a05a818cc57 100644 --- a/packages/common/interfaces/middleware/index.ts +++ b/packages/common/interfaces/middleware/index.ts @@ -1,4 +1,4 @@ -export * from './middleware-config-proxy.interface'; -export * from './middleware-configuration.interface'; -export * from './middleware-consumer.interface'; -export * from './nest-middleware.interface'; +export * from './middleware-config-proxy.interface.js'; +export * from './middleware-configuration.interface.js'; +export * from './middleware-consumer.interface.js'; +export * from './nest-middleware.interface.js'; diff --git a/packages/common/interfaces/middleware/middleware-config-proxy.interface.ts b/packages/common/interfaces/middleware/middleware-config-proxy.interface.ts index ca439207bcf..749b55dccc5 100644 --- a/packages/common/interfaces/middleware/middleware-config-proxy.interface.ts +++ b/packages/common/interfaces/middleware/middleware-config-proxy.interface.ts @@ -1,6 +1,6 @@ -import { Type } from '../type.interface'; -import { RouteInfo } from './middleware-configuration.interface'; -import { MiddlewareConsumer } from './middleware-consumer.interface'; +import { Type } from '../type.interface.js'; +import { RouteInfo } from './middleware-configuration.interface.js'; +import { MiddlewareConsumer } from './middleware-consumer.interface.js'; /** * @publicApi diff --git a/packages/common/interfaces/middleware/middleware-configuration.interface.ts b/packages/common/interfaces/middleware/middleware-configuration.interface.ts index 15414b6cb07..a2ffa3f0700 100644 --- a/packages/common/interfaces/middleware/middleware-configuration.interface.ts +++ b/packages/common/interfaces/middleware/middleware-configuration.interface.ts @@ -1,6 +1,6 @@ -import { RequestMethod } from '../../enums'; -import { Type } from '../type.interface'; -import { VersionValue } from '../version-options.interface'; +import { RequestMethod } from '../../enums/index.js'; +import { Type } from '../type.interface.js'; +import { VersionValue } from '../version-options.interface.js'; export interface RouteInfo { path: string; diff --git a/packages/common/interfaces/middleware/middleware-consumer.interface.ts b/packages/common/interfaces/middleware/middleware-consumer.interface.ts index 21bcbc5e9bc..7ef6b4fff52 100644 --- a/packages/common/interfaces/middleware/middleware-consumer.interface.ts +++ b/packages/common/interfaces/middleware/middleware-consumer.interface.ts @@ -1,5 +1,5 @@ -import { Type } from '../type.interface'; -import { MiddlewareConfigProxy } from './middleware-config-proxy.interface'; +import { Type } from '../type.interface.js'; +import { MiddlewareConfigProxy } from './middleware-config-proxy.interface.js'; /** * Interface defining method for applying user defined middleware to routes. diff --git a/packages/common/interfaces/modules/dynamic-module.interface.ts b/packages/common/interfaces/modules/dynamic-module.interface.ts index 65addedb5ae..2e1d189611a 100644 --- a/packages/common/interfaces/modules/dynamic-module.interface.ts +++ b/packages/common/interfaces/modules/dynamic-module.interface.ts @@ -1,5 +1,5 @@ -import { Type } from '../type.interface'; -import { ModuleMetadata } from './module-metadata.interface'; +import { Type } from '../type.interface.js'; +import { ModuleMetadata } from './module-metadata.interface.js'; /** * Interface defining a Dynamic Module. diff --git a/packages/common/interfaces/modules/index.ts b/packages/common/interfaces/modules/index.ts index 458f952a391..dabf6661965 100644 --- a/packages/common/interfaces/modules/index.ts +++ b/packages/common/interfaces/modules/index.ts @@ -1,8 +1,8 @@ -export * from './dynamic-module.interface'; -export * from './forward-reference.interface'; -export * from './injection-token.interface'; -export * from './introspection-result.interface'; -export * from './module-metadata.interface'; -export * from './nest-module.interface'; -export * from './optional-factory-dependency.interface'; -export * from './provider.interface'; +export * from './dynamic-module.interface.js'; +export * from './forward-reference.interface.js'; +export * from './injection-token.interface.js'; +export * from './introspection-result.interface.js'; +export * from './module-metadata.interface.js'; +export * from './nest-module.interface.js'; +export * from './optional-factory-dependency.interface.js'; +export * from './provider.interface.js'; diff --git a/packages/common/interfaces/modules/injection-token.interface.ts b/packages/common/interfaces/modules/injection-token.interface.ts index ba5c2ce2b14..e0ecd95cbba 100644 --- a/packages/common/interfaces/modules/injection-token.interface.ts +++ b/packages/common/interfaces/modules/injection-token.interface.ts @@ -1,5 +1,5 @@ -import { Abstract } from '../abstract.interface'; -import { Type } from '../type.interface'; +import { Abstract } from '../abstract.interface.js'; +import { Type } from '../type.interface.js'; /** * @publicApi diff --git a/packages/common/interfaces/modules/introspection-result.interface.ts b/packages/common/interfaces/modules/introspection-result.interface.ts index 629aa019505..83f154a5e08 100644 --- a/packages/common/interfaces/modules/introspection-result.interface.ts +++ b/packages/common/interfaces/modules/introspection-result.interface.ts @@ -1,4 +1,4 @@ -import { Scope } from '../scope-options.interface'; +import { Scope } from '../scope-options.interface.js'; /** * @publicApi diff --git a/packages/common/interfaces/modules/module-metadata.interface.ts b/packages/common/interfaces/modules/module-metadata.interface.ts index 2baa7d84fad..2f110f76c69 100644 --- a/packages/common/interfaces/modules/module-metadata.interface.ts +++ b/packages/common/interfaces/modules/module-metadata.interface.ts @@ -1,8 +1,8 @@ -import { Abstract } from '../abstract.interface'; -import { Type } from '../type.interface'; -import { DynamicModule } from './dynamic-module.interface'; -import { ForwardReference } from './forward-reference.interface'; -import { Provider } from './provider.interface'; +import { Abstract } from '../abstract.interface.js'; +import { Type } from '../type.interface.js'; +import { DynamicModule } from './dynamic-module.interface.js'; +import { ForwardReference } from './forward-reference.interface.js'; +import { Provider } from './provider.interface.js'; /** * Interface defining the property object that describes the module. diff --git a/packages/common/interfaces/modules/nest-module.interface.ts b/packages/common/interfaces/modules/nest-module.interface.ts index 236658017a2..186b5c46189 100644 --- a/packages/common/interfaces/modules/nest-module.interface.ts +++ b/packages/common/interfaces/modules/nest-module.interface.ts @@ -1,4 +1,4 @@ -import { MiddlewareConsumer } from '../middleware/middleware-consumer.interface'; +import { MiddlewareConsumer } from '../middleware/middleware-consumer.interface.js'; /** * @publicApi diff --git a/packages/common/interfaces/modules/optional-factory-dependency.interface.ts b/packages/common/interfaces/modules/optional-factory-dependency.interface.ts index 6c8db900ee4..72d97cb25ae 100644 --- a/packages/common/interfaces/modules/optional-factory-dependency.interface.ts +++ b/packages/common/interfaces/modules/optional-factory-dependency.interface.ts @@ -1,4 +1,4 @@ -import { InjectionToken } from './injection-token.interface'; +import { InjectionToken } from './injection-token.interface.js'; /** * @publicApi diff --git a/packages/common/interfaces/modules/provider.interface.ts b/packages/common/interfaces/modules/provider.interface.ts index 2fd7f85b66a..7263d24ba18 100644 --- a/packages/common/interfaces/modules/provider.interface.ts +++ b/packages/common/interfaces/modules/provider.interface.ts @@ -1,7 +1,7 @@ -import { Scope } from '../scope-options.interface'; -import { Type } from '../type.interface'; -import { InjectionToken } from './injection-token.interface'; -import { OptionalFactoryDependency } from './optional-factory-dependency.interface'; +import { Scope } from '../scope-options.interface.js'; +import { Type } from '../type.interface.js'; +import { InjectionToken } from './injection-token.interface.js'; +import { OptionalFactoryDependency } from './optional-factory-dependency.interface.js'; /** * diff --git a/packages/common/interfaces/nest-application-context-options.interface.ts b/packages/common/interfaces/nest-application-context-options.interface.ts index 98aad7391af..99b3c146399 100644 --- a/packages/common/interfaces/nest-application-context-options.interface.ts +++ b/packages/common/interfaces/nest-application-context-options.interface.ts @@ -1,4 +1,4 @@ -import { LoggerService, LogLevel } from '../services/logger.service'; +import { LoggerService, LogLevel } from '../services/logger.service.js'; /** * @publicApi diff --git a/packages/common/interfaces/nest-application-context.interface.ts b/packages/common/interfaces/nest-application-context.interface.ts index 3c2d5c4a32a..0253ecb5c3a 100644 --- a/packages/common/interfaces/nest-application-context.interface.ts +++ b/packages/common/interfaces/nest-application-context.interface.ts @@ -1,9 +1,9 @@ -import { ShutdownSignal } from '../enums/shutdown-signal.enum'; -import { LoggerService, LogLevel } from '../services/logger.service'; -import { DynamicModule } from './modules'; -import { NestApplicationContextOptions } from './nest-application-context-options.interface'; -import { ShutdownHooksOptions } from './shutdown-hooks-options.interface'; -import { Type } from './type.interface'; +import { ShutdownSignal } from '../enums/shutdown-signal.enum.js'; +import { LoggerService, LogLevel } from '../services/logger.service.js'; +import { DynamicModule } from './modules/index.js'; +import { NestApplicationContextOptions } from './nest-application-context-options.interface.js'; +import { ShutdownHooksOptions } from './shutdown-hooks-options.interface.js'; +import { Type } from './type.interface.js'; export type SelectOptions = Pick; diff --git a/packages/common/interfaces/nest-application-options.interface.ts b/packages/common/interfaces/nest-application-options.interface.ts index 5d4c89cbc38..bcf5e641e0c 100644 --- a/packages/common/interfaces/nest-application-options.interface.ts +++ b/packages/common/interfaces/nest-application-options.interface.ts @@ -1,9 +1,9 @@ import { CorsOptions, CorsOptionsDelegate, -} from './external/cors-options.interface'; -import { HttpsOptions } from './external/https-options.interface'; -import { NestApplicationContextOptions } from './nest-application-context-options.interface'; +} from './external/cors-options.interface.js'; +import { HttpsOptions } from './external/https-options.interface.js'; +import { NestApplicationContextOptions } from './nest-application-context-options.interface.js'; /** * @publicApi @@ -30,4 +30,10 @@ export interface NestApplicationOptions extends NestApplicationContextOptions { * keep-alive connections in the HTTP adapter. */ forceCloseConnections?: boolean; + /** + * Whether to return 503 Service Unavailable for new requests during the shutdown process, + * while allowing existing in-flight requests to complete. + * @default false + */ + return503OnClosing?: boolean; } diff --git a/packages/common/interfaces/nest-application.interface.ts b/packages/common/interfaces/nest-application.interface.ts index 94bf44a925f..9c3e7cae4d6 100644 --- a/packages/common/interfaces/nest-application.interface.ts +++ b/packages/common/interfaces/nest-application.interface.ts @@ -1,16 +1,16 @@ -import { CanActivate } from './features/can-activate.interface'; -import { NestInterceptor } from './features/nest-interceptor.interface'; -import { GlobalPrefixOptions } from './global-prefix-options.interface'; -import { HttpServer } from './http/http-server.interface'; +import { CanActivate } from './features/can-activate.interface.js'; +import { NestInterceptor } from './features/nest-interceptor.interface.js'; +import { GlobalPrefixOptions } from './global-prefix-options.interface.js'; +import { HttpServer } from './http/http-server.interface.js'; import { ExceptionFilter, INestMicroservice, NestHybridApplicationOptions, PipeTransform, -} from './index'; -import { INestApplicationContext } from './nest-application-context.interface'; -import { VersioningOptions } from './version-options.interface'; -import { WebSocketAdapter } from './websockets/web-socket-adapter.interface'; +} from './index.js'; +import { INestApplicationContext } from './nest-application-context.interface.js'; +import { VersioningOptions } from './version-options.interface.js'; +import { WebSocketAdapter } from './websockets/web-socket-adapter.interface.js'; /** * Interface defining the core NestApplication object. diff --git a/packages/common/interfaces/nest-microservice.interface.ts b/packages/common/interfaces/nest-microservice.interface.ts index efcaa0761f5..742f192dabb 100644 --- a/packages/common/interfaces/nest-microservice.interface.ts +++ b/packages/common/interfaces/nest-microservice.interface.ts @@ -1,10 +1,11 @@ import { Observable } from 'rxjs'; -import { ExceptionFilter } from './exceptions/exception-filter.interface'; -import { CanActivate } from './features/can-activate.interface'; -import { NestInterceptor } from './features/nest-interceptor.interface'; -import { PipeTransform } from './features/pipe-transform.interface'; -import { INestApplicationContext } from './nest-application-context.interface'; -import { WebSocketAdapter } from './websockets/web-socket-adapter.interface'; +import { ExceptionFilter } from './exceptions/exception-filter.interface.js'; +import { CanActivate } from './features/can-activate.interface.js'; +import { NestInterceptor } from './features/nest-interceptor.interface.js'; +import { PipeTransform } from './features/pipe-transform.interface.js'; +import { PreRequestHook } from './microservices/pre-request-hook.interface.js'; +import { INestApplicationContext } from './nest-application-context.interface.js'; +import { WebSocketAdapter } from './websockets/web-socket-adapter.interface.js'; /** * Interface describing Microservice Context. @@ -56,6 +57,15 @@ export interface INestMicroservice extends INestApplicationContext { */ useGlobalGuards(...guards: CanActivate[]): this; + /** + * Registers a global preRequest hook (executed before all enhancers for every pattern handler). + * Hooks receive an `ExecutionContext` and a `next` function that executes the rest of the pipeline. + * Useful for setting up AsyncLocalStorage context, tracing, or correlation IDs. + * + * @param {...PreRequestHook} hooks + */ + registerPreRequestHook(...hooks: PreRequestHook[]): this; + /** * Terminates the application. * diff --git a/packages/common/interfaces/version-options.interface.ts b/packages/common/interfaces/version-options.interface.ts index fc5a53b6e17..cc9385e8bd5 100644 --- a/packages/common/interfaces/version-options.interface.ts +++ b/packages/common/interfaces/version-options.interface.ts @@ -1,4 +1,4 @@ -import { VersioningType } from '../enums/version-type.enum'; +import { VersioningType } from '../enums/version-type.enum.js'; /** * Indicates that this will work for any version passed in the request, or no version. diff --git a/packages/common/internal.ts b/packages/common/internal.ts new file mode 100644 index 00000000000..a6a02930f33 --- /dev/null +++ b/packages/common/internal.ts @@ -0,0 +1,54 @@ +/** + * Internal module - not part of the public API. + * These exports are used by sibling @nestjs packages. + * Do not depend on these in your application code. + * @internal + * @module + */ + +// Constants +export * from './constants.js'; + +// Enums (internal) +export { RouteParamtypes } from './enums/route-paramtypes.enum.js'; + +// Utils +export * from './utils/shared.utils.js'; +export * from './utils/load-package.util.js'; +export * from './utils/cli-colors.util.js'; +export * from './utils/random-string-generator.util.js'; +export * from './utils/select-exception-filter-metadata.util.js'; + +// Interfaces (types not exposed at root due to name conflicts or internal use) +export type { Controller, Injectable } from './interfaces/index.js'; +export type { NestApplicationContextOptions } from './interfaces/nest-application-context-options.interface.js'; +export type { NestMicroserviceOptions } from './interfaces/microservices/nest-microservice-options.interface.js'; +export type { + CorsOptions, + CorsOptionsDelegate, + CustomOrigin, +} from './interfaces/external/cors-options.interface.js'; +export type { ExceptionFilterMetadata } from './interfaces/exceptions/exception-filter-metadata.interface.js'; +export type { RpcExceptionFilterMetadata } from './interfaces/exceptions/rpc-exception-filter-metadata.interface.js'; +export type { VersionValue } from './interfaces/version-options.interface.js'; +export type { GlobalPrefixOptions } from './interfaces/global-prefix-options.interface.js'; +export type { + MiddlewareConfiguration, + RouteInfo, +} from './interfaces/middleware/middleware-configuration.interface.js'; +export type { MiddlewareConfigProxy } from './interfaces/middleware/middleware-config-proxy.interface.js'; +export type { ModuleMetadata } from './interfaces/modules/module-metadata.interface.js'; +export type { + HttpArgumentsHost, + RpcArgumentsHost, + WsArgumentsHost, +} from './interfaces/features/arguments-host.interface.js'; +export type { RequestHandler } from './interfaces/http/http-server.interface.js'; +export type { + GetOrResolveOptions, + SelectOptions, +} from './interfaces/nest-application-context.interface.js'; +export type { ShutdownHooksOptions } from './interfaces/shutdown-hooks-options.interface.js'; + +// Decorators (internal) +export { assignMetadata } from './decorators/http/route-params.decorator.js'; diff --git a/packages/common/module-utils/configurable-module.builder.ts b/packages/common/module-utils/configurable-module.builder.ts index d7900d3914f..84c4735c071 100644 --- a/packages/common/module-utils/configurable-module.builder.ts +++ b/packages/common/module-utils/configurable-module.builder.ts @@ -1,21 +1,24 @@ /* eslint-disable @typescript-eslint/no-empty-object-type */ -import { DynamicModule, Provider } from '../interfaces'; -import { Logger } from '../services/logger.service'; -import { randomStringGenerator } from '../utils/random-string-generator.util'; +import { DynamicModule, Provider } from '../interfaces/index.js'; +import { Logger } from '../services/logger.service.js'; +import { randomStringGenerator } from '../utils/random-string-generator.util.js'; import { ASYNC_METHOD_SUFFIX, ASYNC_OPTIONS_METADATA_KEYS, CONFIGURABLE_MODULE_ID, DEFAULT_FACTORY_CLASS_METHOD_KEY, DEFAULT_METHOD_KEY, -} from './constants'; +} from './constants.js'; import { ConfigurableModuleAsyncOptions, ConfigurableModuleCls, ConfigurableModuleHost, ConfigurableModuleOptionsFactory, -} from './interfaces'; -import { generateOptionsInjectionToken, getInjectionProviders } from './utils'; +} from './interfaces/index.js'; +import { + generateOptionsInjectionToken, + getInjectionProviders, +} from './utils/index.js'; /** * @publicApi diff --git a/packages/common/module-utils/index.ts b/packages/common/module-utils/index.ts index 4393992bd15..53342dbf97d 100644 --- a/packages/common/module-utils/index.ts +++ b/packages/common/module-utils/index.ts @@ -1,2 +1,2 @@ -export * from './configurable-module.builder'; -export * from './interfaces'; +export * from './configurable-module.builder.js'; +export * from './interfaces/index.js'; diff --git a/packages/common/module-utils/interfaces/configurable-module-async-options.interface.ts b/packages/common/module-utils/interfaces/configurable-module-async-options.interface.ts index 8adf58a17dd..c92b9a35e12 100644 --- a/packages/common/module-utils/interfaces/configurable-module-async-options.interface.ts +++ b/packages/common/module-utils/interfaces/configurable-module-async-options.interface.ts @@ -3,8 +3,8 @@ import { ModuleMetadata, Provider, Type, -} from '../../interfaces'; -import { DEFAULT_FACTORY_CLASS_METHOD_KEY } from '../constants'; +} from '../../interfaces/index.js'; +import { DEFAULT_FACTORY_CLASS_METHOD_KEY } from '../constants.js'; /** * Interface that must be implemented by the module options factory class. diff --git a/packages/common/module-utils/interfaces/configurable-module-cls.interface.ts b/packages/common/module-utils/interfaces/configurable-module-cls.interface.ts index 00ac0314530..895a44e71fd 100644 --- a/packages/common/module-utils/interfaces/configurable-module-cls.interface.ts +++ b/packages/common/module-utils/interfaces/configurable-module-cls.interface.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-empty-object-type */ -import { DynamicModule } from '../../interfaces'; +import { DynamicModule } from '../../interfaces/index.js'; import { DEFAULT_FACTORY_CLASS_METHOD_KEY, DEFAULT_METHOD_KEY, -} from '../constants'; -import { ConfigurableModuleAsyncOptions } from './configurable-module-async-options.interface'; +} from '../constants.js'; +import { ConfigurableModuleAsyncOptions } from './configurable-module-async-options.interface.js'; /** * Class that represents a blueprint/prototype for a configurable Nest module. diff --git a/packages/common/module-utils/interfaces/configurable-module-host.interface.ts b/packages/common/module-utils/interfaces/configurable-module-host.interface.ts index fb23d2d2a93..d2476f725c9 100644 --- a/packages/common/module-utils/interfaces/configurable-module-host.interface.ts +++ b/packages/common/module-utils/interfaces/configurable-module-host.interface.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-empty-object-type */ -import { ConfigurableModuleAsyncOptions } from './configurable-module-async-options.interface'; -import { ConfigurableModuleCls } from './configurable-module-cls.interface'; +import { ConfigurableModuleAsyncOptions } from './configurable-module-async-options.interface.js'; +import { ConfigurableModuleCls } from './configurable-module-cls.interface.js'; /** * Configurable module host. See properties for more details diff --git a/packages/common/module-utils/interfaces/index.ts b/packages/common/module-utils/interfaces/index.ts index 35c52307638..ad104aa6aaa 100644 --- a/packages/common/module-utils/interfaces/index.ts +++ b/packages/common/module-utils/interfaces/index.ts @@ -1,3 +1,3 @@ -export * from './configurable-module-async-options.interface'; -export * from './configurable-module-cls.interface'; -export * from './configurable-module-host.interface'; +export * from './configurable-module-async-options.interface.js'; +export * from './configurable-module-cls.interface.js'; +export * from './configurable-module-host.interface.js'; diff --git a/packages/common/module-utils/utils/generate-options-injection-token.util.ts b/packages/common/module-utils/utils/generate-options-injection-token.util.ts index 259a6afa33e..ae47a1b88c8 100644 --- a/packages/common/module-utils/utils/generate-options-injection-token.util.ts +++ b/packages/common/module-utils/utils/generate-options-injection-token.util.ts @@ -1,4 +1,4 @@ -import { randomStringGenerator } from '../../utils/random-string-generator.util'; +import { randomStringGenerator } from '../../utils/random-string-generator.util.js'; export function generateOptionsInjectionToken() { const hash = randomStringGenerator(); diff --git a/packages/common/module-utils/utils/get-injection-providers.util.ts b/packages/common/module-utils/utils/get-injection-providers.util.ts index f4057d2034d..60ca0f34b64 100644 --- a/packages/common/module-utils/utils/get-injection-providers.util.ts +++ b/packages/common/module-utils/utils/get-injection-providers.util.ts @@ -1,10 +1,10 @@ -import { isUndefined } from '../../utils/shared.utils'; +import { isUndefined } from '../../utils/shared.utils.js'; import { FactoryProvider, InjectionToken, OptionalFactoryDependency, Provider, -} from '../../interfaces'; +} from '../../interfaces/index.js'; /** * @param value diff --git a/packages/common/module-utils/utils/index.ts b/packages/common/module-utils/utils/index.ts index 7125be0f748..0a229ec55d2 100644 --- a/packages/common/module-utils/utils/index.ts +++ b/packages/common/module-utils/utils/index.ts @@ -1,2 +1,2 @@ -export * from './generate-options-injection-token.util'; -export * from './get-injection-providers.util'; +export * from './generate-options-injection-token.util.js'; +export * from './get-injection-providers.util.js'; diff --git a/packages/common/package.json b/packages/common/package.json index e1779a8ed8d..e658f0743e4 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -17,6 +17,14 @@ "access": "public" }, "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./internal": "./internal.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "dependencies": { "file-type": "21.3.0", "iterare": "1.2.1", diff --git a/packages/common/pipes/default-value.pipe.ts b/packages/common/pipes/default-value.pipe.ts index 6afadf5e049..f8babe26204 100644 --- a/packages/common/pipes/default-value.pipe.ts +++ b/packages/common/pipes/default-value.pipe.ts @@ -1,9 +1,9 @@ -import { Injectable } from '../decorators/core/injectable.decorator'; +import { Injectable } from '../decorators/core/injectable.decorator.js'; import { ArgumentMetadata, PipeTransform, -} from '../interfaces/features/pipe-transform.interface'; -import { isNil, isNumber } from '../utils/shared.utils'; +} from '../interfaces/features/pipe-transform.interface.js'; +import { isNil, isNumber } from '../utils/shared.utils.js'; /** * Defines the built-in DefaultValue Pipe diff --git a/packages/common/pipes/file/file-type.validator.ts b/packages/common/pipes/file/file-type.validator.ts index fcd100d2e3c..a03cb73e3e1 100644 --- a/packages/common/pipes/file/file-type.validator.ts +++ b/packages/common/pipes/file/file-type.validator.ts @@ -1,9 +1,7 @@ -import { pathToFileURL } from 'url'; -import { Logger } from '../../services/logger.service'; -import { FileValidatorContext } from './file-validator-context.interface'; -import { FileValidator } from './file-validator.interface'; -import { IFile } from './interfaces'; -import { loadEsm } from 'load-esm'; +import { Logger } from '../../services/logger.service.js'; +import { FileValidatorContext } from './file-validator-context.interface.js'; +import { FileValidator } from './file-validator.interface.js'; +import { IFile } from './interfaces/index.js'; const logger = new Logger('FileTypeValidator'); type FileTypeValidatorContext = FileValidatorContext< @@ -124,15 +122,7 @@ export class FileTypeValidator extends FileValidator< } try { - let fileTypeModule: string; - try { - const resolvedPath = require.resolve('file-type'); - fileTypeModule = pathToFileURL(resolvedPath).href; - } catch { - fileTypeModule = 'file-type'; - } - const { fileTypeFromBuffer } = - await loadEsm(fileTypeModule); + const { fileTypeFromBuffer } = await import('file-type'); const fileType = await fileTypeFromBuffer(file.buffer); if (fileType) { diff --git a/packages/common/pipes/file/file-validator-context.interface.ts b/packages/common/pipes/file/file-validator-context.interface.ts index cecec24e965..67211bb2ced 100644 --- a/packages/common/pipes/file/file-validator-context.interface.ts +++ b/packages/common/pipes/file/file-validator-context.interface.ts @@ -1,4 +1,4 @@ -import { IFile } from './interfaces'; +import { IFile } from './interfaces/index.js'; export type FileValidatorContext = { file?: IFile; diff --git a/packages/common/pipes/file/file-validator.interface.ts b/packages/common/pipes/file/file-validator.interface.ts index 326a55f72cd..c7729a30048 100644 --- a/packages/common/pipes/file/file-validator.interface.ts +++ b/packages/common/pipes/file/file-validator.interface.ts @@ -1,4 +1,4 @@ -import { IFile } from './interfaces'; +import { IFile } from './interfaces/index.js'; /** * Interface describing FileValidators, which can be added to a ParseFilePipe diff --git a/packages/common/pipes/file/index.ts b/packages/common/pipes/file/index.ts index 11d19e3188e..b0da6849bd6 100644 --- a/packages/common/pipes/file/index.ts +++ b/packages/common/pipes/file/index.ts @@ -1,6 +1,6 @@ -export * from './file-type.validator'; -export * from './file-validator.interface'; -export * from './max-file-size.validator'; -export * from './parse-file-options.interface'; -export * from './parse-file.pipe'; -export * from './parse-file-pipe.builder'; +export * from './file-type.validator.js'; +export * from './file-validator.interface.js'; +export * from './max-file-size.validator.js'; +export * from './parse-file-options.interface.js'; +export * from './parse-file.pipe.js'; +export * from './parse-file-pipe.builder.js'; diff --git a/packages/common/pipes/file/interfaces/index.ts b/packages/common/pipes/file/interfaces/index.ts index 09d8f3ca8f1..4b8a5139193 100644 --- a/packages/common/pipes/file/interfaces/index.ts +++ b/packages/common/pipes/file/interfaces/index.ts @@ -1 +1 @@ -export * from './file.interface'; +export * from './file.interface.js'; diff --git a/packages/common/pipes/file/max-file-size.validator.ts b/packages/common/pipes/file/max-file-size.validator.ts index 44baeddefa4..54c8d5d0b77 100644 --- a/packages/common/pipes/file/max-file-size.validator.ts +++ b/packages/common/pipes/file/max-file-size.validator.ts @@ -1,6 +1,6 @@ -import { FileValidatorContext } from './file-validator-context.interface'; -import { FileValidator } from './file-validator.interface'; -import { IFile } from './interfaces'; +import { FileValidatorContext } from './file-validator-context.interface.js'; +import { FileValidator } from './file-validator.interface.js'; +import { IFile } from './interfaces/index.js'; type MaxFileSizeValidatorContext = FileValidatorContext< Omit diff --git a/packages/common/pipes/file/parse-file-options.interface.ts b/packages/common/pipes/file/parse-file-options.interface.ts index f9c67daa876..c7775953a75 100644 --- a/packages/common/pipes/file/parse-file-options.interface.ts +++ b/packages/common/pipes/file/parse-file-options.interface.ts @@ -1,5 +1,5 @@ -import { ErrorHttpStatusCode } from '../../utils/http-error-by-code.util'; -import { FileValidator } from './file-validator.interface'; +import { ErrorHttpStatusCode } from '../../utils/http-error-by-code.util.js'; +import { FileValidator } from './file-validator.interface.js'; /** * @publicApi diff --git a/packages/common/pipes/file/parse-file-pipe.builder.ts b/packages/common/pipes/file/parse-file-pipe.builder.ts index 9199fec3d9c..d5d4810adb2 100644 --- a/packages/common/pipes/file/parse-file-pipe.builder.ts +++ b/packages/common/pipes/file/parse-file-pipe.builder.ts @@ -1,14 +1,14 @@ import { FileTypeValidator, FileTypeValidatorOptions, -} from './file-type.validator'; -import { FileValidator } from './file-validator.interface'; +} from './file-type.validator.js'; +import { FileValidator } from './file-validator.interface.js'; import { MaxFileSizeValidator, MaxFileSizeValidatorOptions, -} from './max-file-size.validator'; -import { ParseFileOptions } from './parse-file-options.interface'; -import { ParseFilePipe } from './parse-file.pipe'; +} from './max-file-size.validator.js'; +import { ParseFileOptions } from './parse-file-options.interface.js'; +import { ParseFilePipe } from './parse-file.pipe.js'; /** * @publicApi diff --git a/packages/common/pipes/file/parse-file.pipe.ts b/packages/common/pipes/file/parse-file.pipe.ts index d2ea60e505b..eda0b857b7b 100644 --- a/packages/common/pipes/file/parse-file.pipe.ts +++ b/packages/common/pipes/file/parse-file.pipe.ts @@ -1,10 +1,14 @@ -import { Injectable, Optional } from '../../decorators/core'; -import { HttpStatus } from '../../enums'; -import { PipeTransform } from '../../interfaces/features/pipe-transform.interface'; -import { HttpErrorByCode } from '../../utils/http-error-by-code.util'; -import { isEmpty, isObject, isUndefined } from '../../utils/shared.utils'; -import { FileValidator } from './file-validator.interface'; -import { ParseFileOptions } from './parse-file-options.interface'; +import { Injectable, Optional } from '../../decorators/core/index.js'; +import { HttpStatus } from '../../enums/index.js'; +import { PipeTransform } from '../../interfaces/features/pipe-transform.interface.js'; +import { HttpErrorByCode } from '../../utils/http-error-by-code.util.js'; +import { + isEmptyArray, + isObject, + isUndefined, +} from '../../utils/shared.utils.js'; +import { FileValidator } from './file-validator.interface.js'; +import { ParseFileOptions } from './parse-file-options.interface.js'; /** * Defines the built-in ParseFile Pipe. This pipe can be used to validate incoming files @@ -17,7 +21,7 @@ import { ParseFileOptions } from './parse-file-options.interface'; * @publicApi */ @Injectable() -export class ParseFilePipe implements PipeTransform { +export class ParseFilePipe implements PipeTransform { protected exceptionFactory: (error: string) => any; private readonly validators: FileValidator[]; private readonly fileIsRequired: boolean; @@ -38,7 +42,7 @@ export class ParseFilePipe implements PipeTransform { this.fileIsRequired = fileIsRequired ?? true; } - async transform(value: any): Promise { + async transform(value: unknown): Promise { const areThereAnyFilesIn = this.thereAreNoFilesIn(value); if (areThereAnyFilesIn && this.fileIsRequired) { @@ -51,7 +55,7 @@ export class ParseFilePipe implements PipeTransform { return value; } - private async validateFilesOrFile(value: any): Promise { + private async validateFilesOrFile(value: unknown): Promise { if (Array.isArray(value)) { await Promise.all(value.map(f => this.validate(f))); } else { @@ -59,21 +63,20 @@ export class ParseFilePipe implements PipeTransform { } } - private thereAreNoFilesIn(value: any): boolean { - const isEmptyArray = Array.isArray(value) && isEmpty(value); - const isEmptyObject = isObject(value) && isEmpty(Object.keys(value)); - return isUndefined(value) || isEmptyArray || isEmptyObject; + private thereAreNoFilesIn(value: unknown): boolean { + const isEmptyObject = isObject(value) && isEmptyArray(Object.keys(value)); + return isUndefined(value) || isEmptyArray(value) || isEmptyObject; } - protected async validate(file: any): Promise { + protected async validate(file: unknown): Promise { for (const validator of this.validators) { await this.validateOrThrow(file, validator); } return file; } - private async validateOrThrow(file: any, validator: FileValidator) { - const isValid = await validator.isValid(file); + private async validateOrThrow(file: unknown, validator: FileValidator) { + const isValid = await validator.isValid(file as any); if (!isValid) { const errorMessage = validator.buildErrorMessage(file); diff --git a/packages/common/pipes/index.ts b/packages/common/pipes/index.ts index f416b9467a7..381366ef3f7 100644 --- a/packages/common/pipes/index.ts +++ b/packages/common/pipes/index.ts @@ -1,10 +1,11 @@ -export * from './default-value.pipe'; -export * from './file'; -export * from './parse-array.pipe'; -export * from './parse-bool.pipe'; -export * from './parse-date.pipe'; -export * from './parse-enum.pipe'; -export * from './parse-float.pipe'; -export * from './parse-int.pipe'; -export * from './parse-uuid.pipe'; -export * from './validation.pipe'; +export * from './default-value.pipe.js'; +export * from './file/index.js'; +export * from './parse-array.pipe.js'; +export * from './parse-bool.pipe.js'; +export * from './parse-date.pipe.js'; +export * from './parse-enum.pipe.js'; +export * from './parse-float.pipe.js'; +export * from './parse-int.pipe.js'; +export * from './parse-uuid.pipe.js'; +export * from './standard-schema-validation.pipe.js'; +export * from './validation.pipe.js'; diff --git a/packages/common/pipes/parse-array.pipe.ts b/packages/common/pipes/parse-array.pipe.ts index 5bc5bd029a3..eb8420d81b3 100644 --- a/packages/common/pipes/parse-array.pipe.ts +++ b/packages/common/pipes/parse-array.pipe.ts @@ -1,14 +1,14 @@ -import { Injectable } from '../decorators/core/injectable.decorator'; -import { Optional } from '../decorators/core/optional.decorator'; -import { HttpStatus } from '../enums/http-status.enum'; -import { Type } from '../interfaces'; +import { Injectable } from '../decorators/core/injectable.decorator.js'; +import { Optional } from '../decorators/core/optional.decorator.js'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { Type } from '../interfaces/index.js'; import { ArgumentMetadata, PipeTransform, -} from '../interfaces/features/pipe-transform.interface'; -import { HttpErrorByCode } from '../utils/http-error-by-code.util'; -import { isNil, isString, isUndefined } from '../utils/shared.utils'; -import { ValidationPipe, ValidationPipeOptions } from './validation.pipe'; +} from '../interfaces/features/pipe-transform.interface.js'; +import { HttpErrorByCode } from '../utils/http-error-by-code.util.js'; +import { isNil, isString, isUndefined } from '../utils/shared.utils.js'; +import { ValidationPipe, ValidationPipeOptions } from './validation.pipe.js'; const VALIDATION_ERROR_MESSAGE = 'Validation failed (parsable array expected)'; const DEFAULT_ARRAY_SEPARATOR = ','; @@ -76,7 +76,7 @@ export class ParseArrayPipe implements PipeTransform { * @param value currently processed route argument * @param metadata contains metadata about the currently processed route argument */ - async transform(value: any, metadata: ArgumentMetadata): Promise { + async transform(value: unknown, metadata: ArgumentMetadata): Promise { if (!value && !this.options.optional) { throw this.exceptionFactory(VALIDATION_ERROR_MESSAGE); } else if (isNil(value) && this.options.optional) { @@ -147,7 +147,9 @@ export class ParseArrayPipe implements PipeTransform { } return targetArray; } else { - value = await Promise.all(value.map(toClassInstance)); + value = await Promise.all( + (value as Array).map(toClassInstance), + ); } } return value; diff --git a/packages/common/pipes/parse-bool.pipe.ts b/packages/common/pipes/parse-bool.pipe.ts index 177ee7cbeeb..415f410786a 100644 --- a/packages/common/pipes/parse-bool.pipe.ts +++ b/packages/common/pipes/parse-bool.pipe.ts @@ -1,15 +1,15 @@ -import { Injectable } from '../decorators/core/injectable.decorator'; -import { Optional } from '../decorators/core/optional.decorator'; -import { HttpStatus } from '../enums/http-status.enum'; +import { Injectable } from '../decorators/core/injectable.decorator.js'; +import { Optional } from '../decorators/core/optional.decorator.js'; +import { HttpStatus } from '../enums/http-status.enum.js'; import { ArgumentMetadata, PipeTransform, -} from '../interfaces/features/pipe-transform.interface'; +} from '../interfaces/features/pipe-transform.interface.js'; import { ErrorHttpStatusCode, HttpErrorByCode, -} from '../utils/http-error-by-code.util'; -import { isNil } from '../utils/shared.utils'; +} from '../utils/http-error-by-code.util.js'; +import { isNil } from '../utils/shared.utils.js'; /** * @publicApi @@ -41,10 +41,7 @@ export interface ParseBoolPipeOptions { * @publicApi */ @Injectable() -export class ParseBoolPipe implements PipeTransform< - string | boolean, - Promise -> { +export class ParseBoolPipe implements PipeTransform { protected exceptionFactory: (error: string) => any; constructor(@Optional() protected readonly options?: ParseBoolPipeOptions) { @@ -64,9 +61,9 @@ export class ParseBoolPipe implements PipeTransform< * @param metadata contains metadata about the currently processed route argument */ async transform( - value: string | boolean, + value: unknown, metadata: ArgumentMetadata, - ): Promise { + ): Promise { if (isNil(value) && this.options?.optional) { return value; } @@ -86,7 +83,7 @@ export class ParseBoolPipe implements PipeTransform< * @returns `true` if `value` is said 'true', ie., if it is equal to the boolean * `true` or the string `"true"` */ - protected isTrue(value: string | boolean): boolean { + protected isTrue(value: unknown): boolean { return value === true || value === 'true'; } @@ -95,7 +92,7 @@ export class ParseBoolPipe implements PipeTransform< * @returns `true` if `value` is said 'false', ie., if it is equal to the boolean * `false` or the string `"false"` */ - protected isFalse(value: string | boolean): boolean { + protected isFalse(value: unknown): boolean { return value === false || value === 'false'; } } diff --git a/packages/common/pipes/parse-date.pipe.ts b/packages/common/pipes/parse-date.pipe.ts index 22f3bf25c50..01fddce6173 100644 --- a/packages/common/pipes/parse-date.pipe.ts +++ b/packages/common/pipes/parse-date.pipe.ts @@ -1,11 +1,11 @@ -import { Injectable } from '../decorators/core/injectable.decorator'; -import { HttpStatus } from '../enums/http-status.enum'; -import { PipeTransform } from '../interfaces/features/pipe-transform.interface'; +import { Injectable } from '../decorators/core/injectable.decorator.js'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { PipeTransform } from '../interfaces/features/pipe-transform.interface.js'; import { ErrorHttpStatusCode, HttpErrorByCode, -} from '../utils/http-error-by-code.util'; -import { isNil } from '../utils/shared.utils'; +} from '../utils/http-error-by-code.util.js'; +import { isNil, isNumber, isString } from '../utils/shared.utils.js'; export interface ParseDatePipeOptions { /** @@ -31,9 +31,7 @@ export interface ParseDatePipeOptions { } @Injectable() -export class ParseDatePipe implements PipeTransform< - string | number | undefined | null -> { +export class ParseDatePipe implements PipeTransform { protected exceptionFactory: (error: string) => any; constructor(private readonly options: ParseDatePipeOptions = {}) { @@ -52,9 +50,7 @@ export class ParseDatePipe implements PipeTransform< * @param value currently processed route argument * @param metadata contains metadata about the currently processed route argument */ - transform( - value: string | number | undefined | null, - ): Date | null | undefined { + transform(value: unknown): Date | null | undefined { if (this.options.optional && isNil(value)) { return this.options.default ? this.options.default() : value; } @@ -63,7 +59,10 @@ export class ParseDatePipe implements PipeTransform< throw this.exceptionFactory('Validation failed (no Date provided)'); } - const transformedValue = new Date(value); + const transformedValue = + isString(value) || isNumber(value) || value instanceof Date + ? new Date(value) + : new Date(NaN); if (isNaN(transformedValue.getTime())) { throw this.exceptionFactory('Validation failed (invalid date format)'); diff --git a/packages/common/pipes/parse-enum.pipe.ts b/packages/common/pipes/parse-enum.pipe.ts index 47f3c263abe..462a0347fd2 100644 --- a/packages/common/pipes/parse-enum.pipe.ts +++ b/packages/common/pipes/parse-enum.pipe.ts @@ -1,11 +1,11 @@ -import { Injectable, Optional } from '../decorators/core'; -import { ArgumentMetadata, HttpStatus } from '../index'; -import { PipeTransform } from '../interfaces/features/pipe-transform.interface'; +import { Injectable, Optional } from '../decorators/core/index.js'; +import { ArgumentMetadata, HttpStatus } from '../index.js'; +import { PipeTransform } from '../interfaces/features/pipe-transform.interface.js'; import { ErrorHttpStatusCode, HttpErrorByCode, -} from '../utils/http-error-by-code.util'; -import { isNil } from '../utils/shared.utils'; +} from '../utils/http-error-by-code.util.js'; +import { isNil } from '../utils/shared.utils.js'; /** * @publicApi @@ -64,7 +64,10 @@ export class ParseEnumPipe implements PipeTransform { * @param value currently processed route argument * @param metadata contains metadata about the currently processed route argument */ - async transform(value: T, metadata: ArgumentMetadata): Promise { + async transform( + value: unknown, + metadata: ArgumentMetadata, + ): Promise { if (isNil(value) && this.options?.optional) { return value; } @@ -73,10 +76,10 @@ export class ParseEnumPipe implements PipeTransform { 'Validation failed (enum string is expected)', ); } - return value; + return value as T; } - protected isEnum(value: T): boolean { + protected isEnum(value: unknown): boolean { const enumValues = Object.keys(this.enumType as object).map( item => this.enumType[item], ); diff --git a/packages/common/pipes/parse-float.pipe.ts b/packages/common/pipes/parse-float.pipe.ts index 04f5e97e27e..ab50468b95d 100644 --- a/packages/common/pipes/parse-float.pipe.ts +++ b/packages/common/pipes/parse-float.pipe.ts @@ -1,11 +1,11 @@ -import { Injectable, Optional } from '../decorators/core'; -import { ArgumentMetadata, HttpStatus } from '../index'; -import { PipeTransform } from '../interfaces/features/pipe-transform.interface'; +import { Injectable, Optional } from '../decorators/core/index.js'; +import { ArgumentMetadata, HttpStatus } from '../index.js'; +import { PipeTransform } from '../interfaces/features/pipe-transform.interface.js'; import { ErrorHttpStatusCode, HttpErrorByCode, -} from '../utils/http-error-by-code.util'; -import { isNil } from '../utils/shared.utils'; +} from '../utils/http-error-by-code.util.js'; +import { isNil } from '../utils/shared.utils.js'; /** * @publicApi @@ -37,7 +37,7 @@ export interface ParseFloatPipeOptions { * @publicApi */ @Injectable() -export class ParseFloatPipe implements PipeTransform { +export class ParseFloatPipe implements PipeTransform { protected exceptionFactory: (error: string) => any; constructor(@Optional() protected readonly options?: ParseFloatPipeOptions) { @@ -57,7 +57,10 @@ export class ParseFloatPipe implements PipeTransform { * @param value currently processed route argument * @param metadata contains metadata about the currently processed route argument */ - async transform(value: string, metadata: ArgumentMetadata): Promise { + async transform( + value: unknown, + metadata: ArgumentMetadata, + ): Promise { if (isNil(value) && this.options?.optional) { return value; } @@ -66,17 +69,17 @@ export class ParseFloatPipe implements PipeTransform { 'Validation failed (numeric string is expected)', ); } - return parseFloat(value); + return parseFloat(String(value)); } /** * @param value currently processed route argument * @returns `true` if `value` is a valid float number */ - protected isNumeric(value: string): boolean { + protected isNumeric(value: unknown): boolean { return ( ['string', 'number'].includes(typeof value) && - !isNaN(parseFloat(value)) && + !isNaN(parseFloat(String(value))) && isFinite(value as any) ); } diff --git a/packages/common/pipes/parse-int.pipe.ts b/packages/common/pipes/parse-int.pipe.ts index d74aa0205ab..1a61933cc82 100644 --- a/packages/common/pipes/parse-int.pipe.ts +++ b/packages/common/pipes/parse-int.pipe.ts @@ -1,15 +1,15 @@ -import { Injectable } from '../decorators/core/injectable.decorator'; -import { Optional } from '../decorators/core/optional.decorator'; -import { HttpStatus } from '../enums/http-status.enum'; +import { Injectable } from '../decorators/core/injectable.decorator.js'; +import { Optional } from '../decorators/core/optional.decorator.js'; +import { HttpStatus } from '../enums/http-status.enum.js'; import { ArgumentMetadata, PipeTransform, -} from '../interfaces/features/pipe-transform.interface'; +} from '../interfaces/features/pipe-transform.interface.js'; import { ErrorHttpStatusCode, HttpErrorByCode, -} from '../utils/http-error-by-code.util'; -import { isNil } from '../utils/shared.utils'; +} from '../utils/http-error-by-code.util.js'; +import { isNil } from '../utils/shared.utils.js'; /** * @publicApi @@ -41,7 +41,7 @@ export interface ParseIntPipeOptions { * @publicApi */ @Injectable() -export class ParseIntPipe implements PipeTransform { +export class ParseIntPipe implements PipeTransform { protected exceptionFactory: (error: string) => any; constructor(@Optional() protected readonly options?: ParseIntPipeOptions) { @@ -61,7 +61,10 @@ export class ParseIntPipe implements PipeTransform { * @param value currently processed route argument * @param metadata contains metadata about the currently processed route argument */ - async transform(value: string, metadata: ArgumentMetadata): Promise { + async transform( + value: unknown, + metadata: ArgumentMetadata, + ): Promise { if (isNil(value) && this.options?.optional) { return value; } @@ -70,17 +73,17 @@ export class ParseIntPipe implements PipeTransform { 'Validation failed (numeric string is expected)', ); } - return parseInt(value, 10); + return parseInt(String(value), 10); } /** * @param value currently processed route argument * @returns `true` if `value` is a valid integer number */ - protected isNumeric(value: string): boolean { + protected isNumeric(value: unknown): boolean { return ( ['string', 'number'].includes(typeof value) && - /^-?\d+$/.test(value) && + /^-?\d+$/.test(String(value)) && isFinite(value as any) ); } diff --git a/packages/common/pipes/parse-uuid.pipe.ts b/packages/common/pipes/parse-uuid.pipe.ts index 1c67860b9cf..6aa72b7a216 100644 --- a/packages/common/pipes/parse-uuid.pipe.ts +++ b/packages/common/pipes/parse-uuid.pipe.ts @@ -1,15 +1,15 @@ -import { Injectable } from '../decorators/core/injectable.decorator'; -import { Optional } from '../decorators/core/optional.decorator'; -import { HttpStatus } from '../enums/http-status.enum'; +import { Injectable } from '../decorators/core/injectable.decorator.js'; +import { Optional } from '../decorators/core/optional.decorator.js'; +import { HttpStatus } from '../enums/http-status.enum.js'; import { ArgumentMetadata, PipeTransform, -} from '../interfaces/features/pipe-transform.interface'; +} from '../interfaces/features/pipe-transform.interface.js'; import { ErrorHttpStatusCode, HttpErrorByCode, -} from '../utils/http-error-by-code.util'; -import { isNil, isString } from '../utils/shared.utils'; +} from '../utils/http-error-by-code.util.js'; +import { isNil, isString } from '../utils/shared.utils.js'; /** * @publicApi @@ -45,7 +45,7 @@ export interface ParseUUIDPipeOptions { * @publicApi */ @Injectable() -export class ParseUUIDPipe implements PipeTransform { +export class ParseUUIDPipe implements PipeTransform { protected static uuidRegExps = { 3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i, 4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i, @@ -70,7 +70,10 @@ export class ParseUUIDPipe implements PipeTransform { (error => new HttpErrorByCode[errorHttpStatusCode](error)); } - async transform(value: string, metadata: ArgumentMetadata): Promise { + async transform( + value: unknown, + metadata: ArgumentMetadata, + ): Promise { if (isNil(value) && this.options?.optional) { return value; } @@ -81,7 +84,7 @@ export class ParseUUIDPipe implements PipeTransform { } is expected)`, ); } - return value; + return value as string; } protected isUUID(str: unknown, version = 'all') { diff --git a/packages/common/pipes/standard-schema-validation.pipe.ts b/packages/common/pipes/standard-schema-validation.pipe.ts new file mode 100644 index 00000000000..38382804cb4 --- /dev/null +++ b/packages/common/pipes/standard-schema-validation.pipe.ts @@ -0,0 +1,192 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import { types } from 'util'; +import { Injectable } from '../decorators/core/injectable.decorator.js'; +import { Optional } from '../decorators/core/optional.decorator.js'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { + ArgumentMetadata, + PipeTransform, +} from '../interfaces/features/pipe-transform.interface.js'; +import { + ErrorHttpStatusCode, + HttpErrorByCode, +} from '../utils/http-error-by-code.util.js'; + +/** + * Built-in JavaScript types that should be excluded from prototype stripping + * to avoid conflicts with test frameworks like Jest's useFakeTimers + */ +const BUILT_IN_TYPES = [Date, RegExp, Error, Map, Set, WeakMap, WeakSet]; + +/** + * @publicApi + */ +export interface StandardSchemaValidationPipeOptions { + /** + * If true, the pipe will return the value produced by the schema + * (which may differ from the input if the schema coerces/transforms values). + * If false, the original input value is returned after successful validation. + * @default true + */ + transform?: boolean; + /** + * If true, the pipe will also validate parameters decorated with custom decorators + * (created with `createParamDecorator`). When false, custom parameters are skipped. + * @default false + */ + validateCustomDecorators?: boolean; + /** + * Options to pass to the standard schema `validate` function. + * These options are forwarded as the second argument to the schema's `~standard.validate` method. + */ + validateOptions?: Record; + /** + * The HTTP status code to be used in the response when the validation fails. + * @default HttpStatus.BAD_REQUEST + */ + errorHttpStatusCode?: ErrorHttpStatusCode; + /** + * A factory function that returns an exception object to be thrown + * if validation fails. + * @param issues The issues returned by the standard schema validation + * @returns The exception object + */ + exceptionFactory?: (issues: readonly StandardSchemaV1.Issue[]) => any; +} + +/** + * Defines the built-in StandardSchemaValidation Pipe. + * + * Uses a standard schema object (conforming to the Standard Schema spec) + * attached to the parameter metadata to validate incoming values. + * + * @see [Standard Schema](https://github.com/standard-schema/standard-schema) + * + * @publicApi + */ +@Injectable() +export class StandardSchemaValidationPipe implements PipeTransform { + protected isTransformEnabled: boolean; + protected validateCustomDecorators: boolean; + protected validateOptions: Record | undefined; + protected exceptionFactory: ( + issues: readonly StandardSchemaV1.Issue[], + ) => any; + + constructor( + @Optional() + protected readonly options?: StandardSchemaValidationPipeOptions, + ) { + const { + transform = true, + validateCustomDecorators = false, + validateOptions, + exceptionFactory, + errorHttpStatusCode = HttpStatus.BAD_REQUEST, + } = options || {}; + + this.isTransformEnabled = transform; + this.validateCustomDecorators = validateCustomDecorators; + this.validateOptions = validateOptions; + + this.exceptionFactory = + exceptionFactory || + (issues => { + const messages = issues.map(issue => issue.message); + return new HttpErrorByCode[errorHttpStatusCode](messages); + }); + } + + /** + * Method that validates the incoming value against the standard schema + * provided in the parameter metadata. + * + * @param value currently processed route argument + * @param metadata contains metadata about the currently processed route argument + */ + async transform(value: T, metadata: ArgumentMetadata): Promise { + const schema = metadata.schema; + if (!schema || !this.toValidate(metadata)) { + return value; + } + + this.stripProtoKeys(value); + + const result = await this.validate(value, schema, this.validateOptions); + + if (result.issues) { + throw this.exceptionFactory(result.issues); + } + return this.isTransformEnabled ? result.value : value; + } + + /** + * Determines whether validation should be performed for the given metadata. + * Skips validation for custom decorators unless `validateCustomDecorators` is enabled. + * + * @param metadata contains metadata about the currently processed route argument + * @returns `true` if validation should be performed + */ + protected toValidate(metadata: ArgumentMetadata): boolean { + const { type } = metadata; + if (type === 'custom' && !this.validateCustomDecorators) { + return false; + } + return true; + } + + /** + * Validates a value against a standard schema. + * Can be overridden to customize validation behavior. + * + * @param value The value to validate + * @param schema The standard schema to validate against + * @param options Optional options forwarded to the schema's validate method + * @returns The validation result + */ + protected validate( + value: unknown, + schema: StandardSchemaV1, + options?: Record, + ): Promise> | StandardSchemaV1.Result { + return schema['~standard'].validate(value, options) as + | Promise> + | StandardSchemaV1.Result; + } + + /** + * Strips dangerous prototype pollution keys from an object. + */ + protected stripProtoKeys(value: any) { + if ( + value == null || + typeof value !== 'object' || + types.isTypedArray(value) + ) { + return; + } + + if (BUILT_IN_TYPES.some(type => value instanceof type)) { + return; + } + + if (Array.isArray(value)) { + for (const v of value) { + this.stripProtoKeys(v); + } + return; + } + + delete value.__proto__; + delete value.prototype; + + const constructorType = value?.constructor; + if (constructorType && !BUILT_IN_TYPES.includes(constructorType)) { + delete value.constructor; + } + + for (const key in value) { + this.stripProtoKeys(value[key]); + } + } +} diff --git a/packages/common/pipes/validation.pipe.ts b/packages/common/pipes/validation.pipe.ts index 3cd36769888..53bac3ed281 100644 --- a/packages/common/pipes/validation.pipe.ts +++ b/packages/common/pipes/validation.pipe.ts @@ -1,24 +1,29 @@ import { iterate } from 'iterare'; import { types } from 'util'; -import { Optional } from '../decorators'; -import { Injectable } from '../decorators/core'; -import { HttpStatus } from '../enums/http-status.enum'; -import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface'; -import { TransformerPackage } from '../interfaces/external/transformer-package.interface'; -import { ValidationError } from '../interfaces/external/validation-error.interface'; -import { ValidatorOptions } from '../interfaces/external/validator-options.interface'; -import { ValidatorPackage } from '../interfaces/external/validator-package.interface'; +import { Injectable } from '../decorators/core/index.js'; +import { Optional } from '../decorators/index.js'; +import { HttpStatus } from '../enums/http-status.enum.js'; +import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface.js'; +import { TransformerPackage } from '../interfaces/external/transformer-package.interface.js'; +import { ValidationError } from '../interfaces/external/validation-error.interface.js'; +import { ValidatorOptions } from '../interfaces/external/validator-options.interface.js'; +import { ValidatorPackage } from '../interfaces/external/validator-package.interface.js'; import { ArgumentMetadata, PipeTransform, -} from '../interfaces/features/pipe-transform.interface'; -import { Type } from '../interfaces/type.interface'; +} from '../interfaces/features/pipe-transform.interface.js'; +import { Type } from '../interfaces/type.interface.js'; import { ErrorHttpStatusCode, HttpErrorByCode, -} from '../utils/http-error-by-code.util'; -import { loadPackage } from '../utils/load-package.util'; -import { isNil, isUndefined } from '../utils/shared.utils'; +} from '../utils/http-error-by-code.util.js'; +import { loadPackage } from '../utils/load-package.util.js'; +import { isNil, isUndefined } from '../utils/shared.utils.js'; + +/** + * @publicApi + */ +export type ValidationErrorFormat = 'list' | 'grouped'; /** * @publicApi @@ -33,10 +38,22 @@ export interface ValidationPipeOptions extends ValidatorOptions { expectedType?: Type; validatorPackage?: ValidatorPackage; transformerPackage?: TransformerPackage; + /** + * Specifies the format of validation error messages. + * - 'list': Returns an array of error message strings (default). The response message is `string[]`. + * - 'grouped': Returns an object with property paths as keys and arrays of unmodified error messages as values. + * The response message is `Record`. Custom messages defined in validation decorators + * (e.g., `@IsNotEmpty({ message: 'Name is required' })`) are preserved without parent path prefixes. + * + * @remarks + * When using 'grouped', the `message` property in the error response changes from `string[]` to `Record`. + * If you have exception filters or interceptors that assume `message` is always an array, they will need to be updated. + */ + errorFormat?: ValidationErrorFormat; } -let classValidator: ValidatorPackage = {} as any; -let classTransformer: TransformerPackage = {} as any; +let classValidator: any = {} as any; +let classTransformer: any = {} as any; /** * Built-in JavaScript types that should be excluded from prototype stripping @@ -50,7 +67,7 @@ const BUILT_IN_TYPES = [Date, RegExp, Error, Map, Set, WeakMap, WeakSet]; * @publicApi */ @Injectable() -export class ValidationPipe implements PipeTransform { +export class ValidationPipe implements PipeTransform { protected isTransformEnabled: boolean; protected isDetailedOutputDisabled?: boolean; protected validatorOptions: ValidatorOptions; @@ -59,6 +76,7 @@ export class ValidationPipe implements PipeTransform { protected expectedType: Type | undefined; protected exceptionFactory: (errors: ValidationError[]) => any; protected validateCustomDecorators: boolean; + protected errorFormat: ValidationErrorFormat; constructor(@Optional() options?: ValidationPipeOptions) { options = options || {}; @@ -69,6 +87,7 @@ export class ValidationPipe implements PipeTransform { expectedType, transformOptions, validateCustomDecorators, + errorFormat, ...validatorOptions } = options; @@ -81,6 +100,7 @@ export class ValidationPipe implements PipeTransform { this.validateCustomDecorators = validateCustomDecorators || false; this.errorHttpStatusCode = errorHttpStatusCode || HttpStatus.BAD_REQUEST; this.expectedType = expectedType; + this.errorFormat = errorFormat || 'list'; this.exceptionFactory = options.exceptionFactory || this.createExceptionFactory(); @@ -90,27 +110,31 @@ export class ValidationPipe implements PipeTransform { protected loadValidator( validatorPackage?: ValidatorPackage, - ): ValidatorPackage { + ): ValidatorPackage | Promise { return ( validatorPackage ?? - loadPackage('class-validator', 'ValidationPipe', () => - require('class-validator'), + loadPackage( + 'class-validator', + 'ValidationPipe', + () => import('class-validator'), ) ); } protected loadTransformer( transformerPackage?: TransformerPackage, - ): TransformerPackage { + ): TransformerPackage | Promise { return ( transformerPackage ?? - loadPackage('class-transformer', 'ValidationPipe', () => - require('class-transformer'), + loadPackage( + 'class-transformer', + 'ValidationPipe', + () => import('class-transformer'), ) ); } - public async transform(value: any, metadata: ArgumentMetadata) { + public async transform(value: unknown, metadata: ArgumentMetadata) { if (this.expectedType) { metadata = { ...metadata, metatype: this.expectedType }; } @@ -121,6 +145,10 @@ export class ValidationPipe implements PipeTransform { ? this.transformPrimitive(value, metadata) : value; } + + classValidator = (await classValidator) as ValidatorPackage; + classTransformer = (await classTransformer) as TransformerPackage; + const originalValue = value; value = this.toEmptyIfNil(value, metatype); @@ -183,6 +211,12 @@ export class ValidationPipe implements PipeTransform { if (this.isDetailedOutputDisabled) { return new HttpErrorByCode[this.errorHttpStatusCode](); } + if (this.errorFormat === 'grouped') { + const errors = this.groupValidationErrors(validationErrors); + return new HttpErrorByCode[this.errorHttpStatusCode]({ + message: errors, + }); + } const errors = this.flattenValidationErrors(validationErrors); return new HttpErrorByCode[this.errorHttpStatusCode](errors); }; @@ -197,7 +231,7 @@ export class ValidationPipe implements PipeTransform { return !types.some(t => metatype === t) && !isNil(metatype); } - protected transformPrimitive(value: any, metadata: ArgumentMetadata) { + protected transformPrimitive(value: unknown, metadata: ArgumentMetadata) { if (!metadata.data) { // leave top-level query/param objects unmodified return value; @@ -223,7 +257,7 @@ export class ValidationPipe implements PipeTransform { // they were not defined return undefined; } - return +value; + return +(value as any); } if (metatype === String && !isUndefined(value)) { return String(value); @@ -232,7 +266,7 @@ export class ValidationPipe implements PipeTransform { } protected toEmptyIfNil( - value: T, + value: unknown, metatype: Type | object, ): R | object | string { if (!isNil(value)) { @@ -310,6 +344,25 @@ export class ValidationPipe implements PipeTransform { .toArray(); } + protected groupValidationErrors( + validationErrors: ValidationError[], + parentPath?: string, + ): Record { + const result: Record = {}; + for (const error of validationErrors) { + const path = parentPath + ? `${parentPath}.${error.property}` + : error.property; + if (error.constraints) { + result[path] = Object.values(error.constraints); + } + if (error.children && error.children.length) { + Object.assign(result, this.groupValidationErrors(error.children, path)); + } + } + return result; + } + protected mapChildrenToValidationErrors( error: ValidationError, parentPath?: string, diff --git a/packages/common/serializer/class-serializer.interceptor.ts b/packages/common/serializer/class-serializer.interceptor.ts index 875bb1a308e..96802f365a9 100644 --- a/packages/common/serializer/class-serializer.interceptor.ts +++ b/packages/common/serializer/class-serializer.interceptor.ts @@ -1,16 +1,20 @@ -import { ClassSerializerContextOptions } from './class-serializer.interfaces'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { Inject, Injectable, Optional } from '../decorators/core'; -import { StreamableFile } from '../file-stream'; -import { CallHandler, ExecutionContext, NestInterceptor } from '../interfaces'; -import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface'; -import { TransformerPackage } from '../interfaces/external/transformer-package.interface'; -import { loadPackage } from '../utils/load-package.util'; -import { isObject } from '../utils/shared.utils'; -import { CLASS_SERIALIZER_OPTIONS } from './class-serializer.constants'; +import { Inject, Injectable, Optional } from '../decorators/core/index.js'; +import { StreamableFile } from '../file-stream/index.js'; +import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface.js'; +import { TransformerPackage } from '../interfaces/external/transformer-package.interface.js'; +import { + CallHandler, + ExecutionContext, + NestInterceptor, +} from '../interfaces/index.js'; +import { loadPackage } from '../utils/load-package.util.js'; +import { isObject } from '../utils/shared.utils.js'; +import { CLASS_SERIALIZER_OPTIONS } from './class-serializer.constants.js'; +import { ClassSerializerContextOptions } from './class-serializer.interfaces.js'; -let classTransformer: TransformerPackage = {} as any; +let classTransformer: any = {} as any; export interface PlainLiteralObject { [key: string]: any; @@ -40,16 +44,19 @@ export class ClassSerializerInterceptor implements NestInterceptor { ) { classTransformer = defaultOptions?.transformerPackage ?? - loadPackage('class-transformer', 'ClassSerializerInterceptor', () => - require('class-transformer'), + loadPackage( + 'class-transformer', + 'ClassSerializerInterceptor', + () => import('class-transformer'), ); - - if (!defaultOptions?.transformerPackage) { - require('class-transformer'); - } } - intercept(context: ExecutionContext, next: CallHandler): Observable { + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + classTransformer = (await classTransformer) as TransformerPackage; + const contextOptions = this.getContextOptions(context); const options = { ...this.defaultOptions, diff --git a/packages/common/serializer/class-serializer.interfaces.ts b/packages/common/serializer/class-serializer.interfaces.ts index c5404177cf8..725c58b7f24 100644 --- a/packages/common/serializer/class-serializer.interfaces.ts +++ b/packages/common/serializer/class-serializer.interfaces.ts @@ -1,5 +1,5 @@ -import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface'; -import { Type } from '../interfaces'; +import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface.js'; +import { Type } from '../interfaces/index.js'; /** * @publicApi diff --git a/packages/common/serializer/decorators/index.ts b/packages/common/serializer/decorators/index.ts index eff9f6fe4aa..3a87339d439 100644 --- a/packages/common/serializer/decorators/index.ts +++ b/packages/common/serializer/decorators/index.ts @@ -1 +1 @@ -export * from './serialize-options.decorator'; +export * from './serialize-options.decorator.js'; diff --git a/packages/common/serializer/decorators/serialize-options.decorator.ts b/packages/common/serializer/decorators/serialize-options.decorator.ts index bd92695fd94..8613a0cf1da 100644 --- a/packages/common/serializer/decorators/serialize-options.decorator.ts +++ b/packages/common/serializer/decorators/serialize-options.decorator.ts @@ -1,9 +1,13 @@ -import { SetMetadata } from '../../decorators'; -import { ClassSerializerContextOptions } from '../class-serializer.interfaces'; -import { CLASS_SERIALIZER_OPTIONS } from '../class-serializer.constants'; +import { SetMetadata } from '../../decorators/index.js'; +import { CLASS_SERIALIZER_OPTIONS } from '../class-serializer.constants.js'; +import { ClassSerializerContextOptions } from '../class-serializer.interfaces.js'; +import { StandardSchemaSerializerContextOptions } from '../standard-schema-serializer.interfaces.js'; /** * @publicApi */ -export const SerializeOptions = (options: ClassSerializerContextOptions) => - SetMetadata(CLASS_SERIALIZER_OPTIONS, options); +export const SerializeOptions = ( + options: + | ClassSerializerContextOptions + | StandardSchemaSerializerContextOptions, +) => SetMetadata(CLASS_SERIALIZER_OPTIONS, options); diff --git a/packages/common/serializer/index.ts b/packages/common/serializer/index.ts index 828c4aec44e..1989c94e569 100644 --- a/packages/common/serializer/index.ts +++ b/packages/common/serializer/index.ts @@ -1,3 +1,5 @@ -export * from './class-serializer.interceptor'; -export * from './decorators'; -export * from './class-serializer.interfaces'; +export * from './class-serializer.interceptor.js'; +export * from './class-serializer.interfaces.js'; +export * from './decorators/index.js'; +export * from './standard-schema-serializer.interceptor.js'; +export * from './standard-schema-serializer.interfaces.js'; diff --git a/packages/common/serializer/standard-schema-serializer.interceptor.ts b/packages/common/serializer/standard-schema-serializer.interceptor.ts new file mode 100644 index 00000000000..90c076f17e9 --- /dev/null +++ b/packages/common/serializer/standard-schema-serializer.interceptor.ts @@ -0,0 +1,132 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Inject, Injectable, Optional } from '../decorators/core/index.js'; +import { StreamableFile } from '../file-stream/index.js'; +import { + CallHandler, + ExecutionContext, + NestInterceptor, +} from '../interfaces/index.js'; +import { isObject } from '../utils/shared.utils.js'; +import { CLASS_SERIALIZER_OPTIONS } from './class-serializer.constants.js'; +import { StandardSchemaSerializerContextOptions } from './standard-schema-serializer.interfaces.js'; + +interface PlainLiteralObject { + [key: string]: any; +} + +// NOTE (external) +// We need to deduplicate them here due to the circular dependency +// between core and common packages +const REFLECTOR = 'Reflector'; + +/** + * @publicApi + */ +export interface StandardSchemaSerializerInterceptorOptions { + /** + * A default standard schema to use for serialization when no schema + * is provided via `@SerializeOptions()`. + */ + schema?: StandardSchemaV1; + /** + * Default options forwarded to the schema's `~standard.validate()` call. + * Can be overridden per-handler via `@SerializeOptions({ validateOptions })`. + */ + validateOptions?: StandardSchemaV1.Options; +} + +/** + * An interceptor that serializes outgoing responses using a Standard Schema. + * + * The schema can be provided either: + * - As a default option in the interceptor constructor + * - Per-handler or per-class via `@SerializeOptions({ schema })` decorator + * + * When a schema is present, the interceptor validates/transforms the response + * through the schema's `~standard.validate()` method. If validation fails, + * the issues are thrown as an error. + * + * @see [Standard Schema](https://github.com/standard-schema/standard-schema) + * + * @publicApi + */ +@Injectable() +export class StandardSchemaSerializerInterceptor implements NestInterceptor { + constructor( + @Inject(REFLECTOR) protected readonly reflector: any, + @Optional() + protected readonly defaultOptions: StandardSchemaSerializerInterceptorOptions = {}, + ) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const contextOptions = this.getContextOptions(context); + const schema = contextOptions?.schema ?? this.defaultOptions.schema; + const validateOptions = + contextOptions?.validateOptions ?? this.defaultOptions.validateOptions; + + return next + .handle() + .pipe( + map((res: PlainLiteralObject | Array) => + this.serialize(res, schema, validateOptions), + ), + ); + } + + /** + * Serializes responses that are non-null objects nor streamable files. + */ + serialize( + response: PlainLiteralObject | Array, + schema: StandardSchemaV1 | undefined, + validateOptions?: StandardSchemaV1.Options, + ): + | PlainLiteralObject + | Array + | Promise> { + if (!schema || !isObject(response) || response instanceof StreamableFile) { + return response; + } + + return Array.isArray(response) + ? Promise.all( + response.map(item => + this.transformToPlain(item, schema, validateOptions), + ), + ) + : this.transformToPlain(response, schema, validateOptions); + } + + async transformToPlain( + plainOrClass: any, + schema: StandardSchemaV1, + validateOptions?: StandardSchemaV1.Options, + ): Promise { + if (!plainOrClass) { + return plainOrClass; + } + + const result = await schema['~standard'].validate( + plainOrClass, + validateOptions, + ); + + if (result.issues) { + throw new Error( + `Serialization failed: ${result.issues.map(i => i.message).join(', ')}`, + ); + } + return result.value as PlainLiteralObject; + } + + protected getContextOptions( + context: ExecutionContext, + ): StandardSchemaSerializerContextOptions | undefined { + return this.reflector.getAllAndOverride(CLASS_SERIALIZER_OPTIONS, [ + context.getHandler(), + context.getClass(), + ]); + } +} diff --git a/packages/common/serializer/standard-schema-serializer.interfaces.ts b/packages/common/serializer/standard-schema-serializer.interfaces.ts new file mode 100644 index 00000000000..95253edf27f --- /dev/null +++ b/packages/common/serializer/standard-schema-serializer.interfaces.ts @@ -0,0 +1,19 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec'; + +/** + * Options for the `StandardSchemaSerializerInterceptor`, passed via + * `@SerializeOptions({ schema })`. + * + * @publicApi + */ +export interface StandardSchemaSerializerContextOptions { + /** + * A standard schema to use for serialization. + * Used by `StandardSchemaSerializerInterceptor` to validate/transform the response. + */ + schema?: StandardSchemaV1; + /** + * Optional options forwarded to the schema's `~standard.validate()` call. + */ + validateOptions?: StandardSchemaV1.Options; +} diff --git a/packages/common/services/console-logger.service.ts b/packages/common/services/console-logger.service.ts index 119e23a8029..b506a04789f 100644 --- a/packages/common/services/console-logger.service.ts +++ b/packages/common/services/console-logger.service.ts @@ -1,14 +1,14 @@ import { inspect, InspectOptions } from 'util'; -import { Injectable, Optional } from '../decorators/core'; -import { clc, yellow, isColorAllowed } from '../utils/cli-colors.util'; +import { Injectable, Optional } from '../decorators/core/index.js'; +import { clc, yellow, isColorAllowed } from '../utils/cli-colors.util.js'; import { isFunction, isPlainObject, isString, isUndefined, -} from '../utils/shared.utils'; -import { LoggerService, LogLevel } from './logger.service'; -import { isLogLevelEnabled } from './utils/is-log-level-enabled.util'; +} from '../utils/shared.utils.js'; +import { LoggerService, LogLevel } from './logger.service.js'; +import { isLogLevelEnabled } from './utils/is-log-level-enabled.util.js'; const DEFAULT_DEPTH = 5; @@ -95,6 +95,20 @@ export interface ConsoleLoggerOptions { * Ignored when `json` is enabled, colors are disabled, and `compact` is set to true as it produces a parseable JSON output. */ breakLength?: number; + /** + * If enabled, plain objects passed after the message are treated as structured + * metadata (params) attached to the log entry, instead of being logged as + * separate messages. + * @default true + */ + structuredParams?: boolean; + /** + * When true and `json` mode is enabled, structured params are spread into the root JSON object + * instead of being nested under a `params` key. + * Requires `structuredParams` to be enabled. + * @default false + */ + flattenParams?: boolean; } const DEFAULT_LOG_LEVELS: LogLevel[] = [ @@ -182,11 +196,11 @@ export class ConsoleLogger implements LoggerService { if (!this.isLevelEnabled('log')) { return; } - const { messages, context } = this.getContextAndMessagesToPrint([ + const { messages, context, params } = this.getContextAndMessagesToPrint([ message, ...optionalParams, ]); - this.printMessages(messages, context, 'log'); + this.printMessages(messages, context, 'log', 'stdout', undefined, params); } /** @@ -200,10 +214,10 @@ export class ConsoleLogger implements LoggerService { if (!this.isLevelEnabled('error')) { return; } - const { messages, context, stack } = + const { messages, context, stack, params } = this.getContextAndStackAndMessagesToPrint([message, ...optionalParams]); - this.printMessages(messages, context, 'error', 'stderr', stack); + this.printMessages(messages, context, 'error', 'stderr', stack, params); this.printStackTrace(stack!); } @@ -217,11 +231,11 @@ export class ConsoleLogger implements LoggerService { if (!this.isLevelEnabled('warn')) { return; } - const { messages, context } = this.getContextAndMessagesToPrint([ + const { messages, context, params } = this.getContextAndMessagesToPrint([ message, ...optionalParams, ]); - this.printMessages(messages, context, 'warn'); + this.printMessages(messages, context, 'warn', 'stdout', undefined, params); } /** @@ -234,11 +248,11 @@ export class ConsoleLogger implements LoggerService { if (!this.isLevelEnabled('debug')) { return; } - const { messages, context } = this.getContextAndMessagesToPrint([ + const { messages, context, params } = this.getContextAndMessagesToPrint([ message, ...optionalParams, ]); - this.printMessages(messages, context, 'debug'); + this.printMessages(messages, context, 'debug', 'stdout', undefined, params); } /** @@ -251,11 +265,18 @@ export class ConsoleLogger implements LoggerService { if (!this.isLevelEnabled('verbose')) { return; } - const { messages, context } = this.getContextAndMessagesToPrint([ + const { messages, context, params } = this.getContextAndMessagesToPrint([ message, ...optionalParams, ]); - this.printMessages(messages, context, 'verbose'); + this.printMessages( + messages, + context, + 'verbose', + 'stdout', + undefined, + params, + ); } /** @@ -268,11 +289,11 @@ export class ConsoleLogger implements LoggerService { if (!this.isLevelEnabled('fatal')) { return; } - const { messages, context } = this.getContextAndMessagesToPrint([ + const { messages, context, params } = this.getContextAndMessagesToPrint([ message, ...optionalParams, ]); - this.printMessages(messages, context, 'fatal'); + this.printMessages(messages, context, 'fatal', 'stdout', undefined, params); } /** @@ -316,6 +337,7 @@ export class ConsoleLogger implements LoggerService { logLevel: LogLevel = 'log', writeStreamType?: 'stdout' | 'stderr', errorStack?: unknown, + params?: Record, ) { messages.forEach(message => { if (this.options.json) { @@ -324,6 +346,7 @@ export class ConsoleLogger implements LoggerService { logLevel, writeStreamType, errorStack, + params, }); return; } @@ -338,6 +361,7 @@ export class ConsoleLogger implements LoggerService { formattedLogLevel, contextMessage, timestampDiff, + params, ); if (this.options.forceConsole) { @@ -359,6 +383,7 @@ export class ConsoleLogger implements LoggerService { logLevel: LogLevel; writeStreamType?: 'stdout' | 'stderr'; errorStack?: unknown; + params?: Record; }, ) { const logObject = this.getJsonLogObject(message, options); @@ -386,6 +411,7 @@ export class ConsoleLogger implements LoggerService { logLevel: LogLevel; writeStreamType?: 'stdout' | 'stderr'; errorStack?: unknown; + params?: Record; }, ) { type JsonLogObject = { @@ -395,6 +421,8 @@ export class ConsoleLogger implements LoggerService { message: unknown; context?: string; stack?: unknown; + params?: Record; + [key: string]: unknown; }; const logObject: JsonLogObject = { @@ -411,6 +439,15 @@ export class ConsoleLogger implements LoggerService { if (options.errorStack) { logObject.stack = options.errorStack; } + + if (options.params) { + if (this.options.flattenParams) { + Object.assign(logObject, options.params); + } else { + logObject.params = options.params; + } + } + return logObject; } @@ -434,11 +471,21 @@ export class ConsoleLogger implements LoggerService { formattedLogLevel: string, contextMessage: string, timestampDiff: string, + params?: Record, ) { const output = this.stringifyMessage(message, logLevel); pidMessage = this.colorize(pidMessage, logLevel); formattedLogLevel = this.colorize(formattedLogLevel, logLevel); - return `${pidMessage}${this.getTimestamp()} ${formattedLogLevel} ${contextMessage}${output}${timestampDiff}\n`; + const paramsOutput = params ? ` ${this.stringifyParams(params)}` : ''; + return `${pidMessage}${this.getTimestamp()} ${formattedLogLevel} ${contextMessage}${output}${paramsOutput}${timestampDiff}\n`; + } + + protected stringifyParams(params: Record): string { + return inspect(params, { + ...this.inspectOptions, + compact: true, + breakLength: Infinity, + }); } protected stringifyMessage(message: unknown, logLevel: LogLevel) { @@ -557,40 +604,81 @@ export class ConsoleLogger implements LoggerService { } const lastElement = args[args.length - 1]; const isContext = isString(lastElement); - if (!isContext) { - return { messages: args, context: this.context }; + + let context: string | undefined; + let remainingArgs: unknown[]; + + if (isContext) { + context = lastElement; + remainingArgs = args.slice(0, args.length - 1); + } else { + context = this.context; + remainingArgs = args; } - return { - context: lastElement, - messages: args.slice(0, args.length - 1), - }; + + if (this.options.structuredParams === false) { + return { messages: remainingArgs, context }; + } + + // Extract plain objects (excluding the first arg which is always the message) as params + const messages: unknown[] = [remainingArgs[0]]; + const paramObjects: Record[] = []; + + for (let i = 1; i < remainingArgs.length; i++) { + if (isPlainObject(remainingArgs[i])) { + paramObjects.push(remainingArgs[i] as Record); + } else { + messages.push(remainingArgs[i]); + } + } + + const params = + paramObjects.length > 0 ? Object.assign({}, ...paramObjects) : undefined; + + return { messages, context, params }; } private getContextAndStackAndMessagesToPrint(args: unknown[]) { if (args.length === 2) { - return this.isStackFormat(args[1]) - ? { - messages: [args[0]], - stack: args[1] as string, - context: this.context, - } - : { ...this.getContextAndMessagesToPrint(args) }; + if (this.isStackFormat(args[1])) { + return { + messages: [args[0]], + stack: args[1] as string, + context: this.context, + }; + } + return { ...this.getContextAndMessagesToPrint(args) }; + } + + const trailingArg = args[args.length - 1]; + if (this.isStackFormat(trailingArg)) { + const { messages, context, params } = this.getContextAndMessagesToPrint( + args.slice(0, -1), + ); + return { + messages, + context, + stack: trailingArg as string, + params, + }; } - const { messages, context } = this.getContextAndMessagesToPrint(args); + const { messages, context, params } = + this.getContextAndMessagesToPrint(args); if (messages?.length <= 1) { - return { messages, context }; + return { messages, context, params }; } const lastElement = messages[messages.length - 1]; const isStack = isString(lastElement); // https://github.com/nestjs/nest/issues/11074#issuecomment-1421680060 if (!isStack && !isUndefined(lastElement)) { - return { messages, context }; + return { messages, context, params }; } return { stack: lastElement, messages: messages.slice(0, messages.length - 1), context, + params, }; } diff --git a/packages/common/services/index.ts b/packages/common/services/index.ts index c1cad965b63..34ba8f13b4a 100644 --- a/packages/common/services/index.ts +++ b/packages/common/services/index.ts @@ -1,3 +1,3 @@ -export * from './console-logger.service'; -export * from './logger.service'; -export * from './utils/filter-log-levels.util'; +export * from './console-logger.service.js'; +export * from './logger.service.js'; +export * from './utils/filter-log-levels.util.js'; diff --git a/packages/common/services/logger.service.ts b/packages/common/services/logger.service.ts index a49f651ffa5..63f5d491e3f 100644 --- a/packages/common/services/logger.service.ts +++ b/packages/common/services/logger.service.ts @@ -1,7 +1,7 @@ -import { Injectable, Optional } from '../decorators/core'; -import { isObject } from '../utils/shared.utils'; -import { ConsoleLogger } from './console-logger.service'; -import { isLogLevelEnabled } from './utils'; +import { Injectable, Optional } from '../decorators/core/index.js'; +import { isObject } from '../utils/shared.utils.js'; +import { ConsoleLogger } from './console-logger.service.js'; +import { isLogLevelEnabled } from './utils/index.js'; export const LOG_LEVELS = [ 'verbose', diff --git a/packages/common/services/utils/filter-log-levels.util.ts b/packages/common/services/utils/filter-log-levels.util.ts index 6c0bd0ff7ff..38c3b2abbc9 100644 --- a/packages/common/services/utils/filter-log-levels.util.ts +++ b/packages/common/services/utils/filter-log-levels.util.ts @@ -1,5 +1,5 @@ -import { LOG_LEVELS, LogLevel } from '../logger.service'; -import { isLogLevel } from './is-log-level.util'; +import { LOG_LEVELS, LogLevel } from '../logger.service.js'; +import { isLogLevel } from './is-log-level.util.js'; /** * @publicApi diff --git a/packages/common/services/utils/index.ts b/packages/common/services/utils/index.ts index e6ad9430160..ff3175fce30 100644 --- a/packages/common/services/utils/index.ts +++ b/packages/common/services/utils/index.ts @@ -1,3 +1,3 @@ -export * from './filter-log-levels.util'; -export * from './is-log-level-enabled.util'; -export * from './is-log-level.util'; +export * from './filter-log-levels.util.js'; +export * from './is-log-level-enabled.util.js'; +export * from './is-log-level.util.js'; diff --git a/packages/common/services/utils/is-log-level-enabled.util.ts b/packages/common/services/utils/is-log-level-enabled.util.ts index b902a1b2e58..283748a0108 100644 --- a/packages/common/services/utils/is-log-level-enabled.util.ts +++ b/packages/common/services/utils/is-log-level-enabled.util.ts @@ -1,4 +1,4 @@ -import { LogLevel } from '../logger.service'; +import { LogLevel } from '../logger.service.js'; const LOG_LEVEL_VALUES: Record = { verbose: 0, diff --git a/packages/common/services/utils/is-log-level.util.ts b/packages/common/services/utils/is-log-level.util.ts index 623e2f274a5..da363f9dda1 100644 --- a/packages/common/services/utils/is-log-level.util.ts +++ b/packages/common/services/utils/is-log-level.util.ts @@ -1,4 +1,4 @@ -import { LOG_LEVELS, LogLevel } from '../logger.service'; +import { LOG_LEVELS, LogLevel } from '../logger.service.js'; /** * @publicApi diff --git a/packages/common/test/decorators/apply-decorators.spec.ts b/packages/common/test/decorators/apply-decorators.spec.ts index d804206cc00..8ed91a750cf 100644 --- a/packages/common/test/decorators/apply-decorators.spec.ts +++ b/packages/common/test/decorators/apply-decorators.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { GUARDS_METADATA } from '../../constants'; -import { applyDecorators, UseGuards } from '../../decorators'; -import { CanActivate } from '../../interfaces'; +import { GUARDS_METADATA } from '../../constants.js'; +import { applyDecorators, UseGuards } from '../../decorators/index.js'; +import { CanActivate } from '../../interfaces/index.js'; describe('applyDecorators', () => { function testDecorator1(param: number) { @@ -54,8 +53,8 @@ describe('applyDecorators', () => { myParam3: 0, }; - expect(decoratedTarget).to.be.deep.equal(expectedTarget); - expect(customDecoratedTarget).to.be.deep.equal(expectedTarget); + expect(decoratedTarget).toEqual(expectedTarget); + expect(customDecoratedTarget).toEqual(expectedTarget); }); }); @@ -89,7 +88,7 @@ describe('applyDecorators @GuardCompositeDecorator', () => { it('should be using the guard defined on the class', () => { const classMetadata = Reflect.getMetadata(GUARDS_METADATA, Test); - expect(classMetadata).to.deep.equal([Guard]); + expect(classMetadata).toEqual([Guard]); }); it('should be using the guard defined on the prototype method', () => { @@ -99,9 +98,9 @@ describe('applyDecorators @GuardCompositeDecorator', () => { const methodMetadata = Reflect.getMetadata(GUARDS_METADATA, instance.test); const instanceMetadata = Reflect.getMetadata(GUARDS_METADATA, instance); - expect(classMetadata).to.be.undefined; - expect(methodMetadata).to.deep.equal([Guard]); - expect(instanceMetadata).to.be.undefined; + expect(classMetadata).toBeUndefined(); + expect(methodMetadata).toEqual([Guard]); + expect(instanceMetadata).toBeUndefined(); }); it('should be using the guard defined on the static method', () => { @@ -114,7 +113,7 @@ describe('applyDecorators @GuardCompositeDecorator', () => { TestWithStaticMethod.test, ); - expect(classMetadata).to.be.undefined; - expect(methodMetadata).to.deep.equal([Guard]); + expect(classMetadata).toBeUndefined(); + expect(methodMetadata).toEqual([Guard]); }); }); diff --git a/packages/common/test/decorators/bind.decorator.spec.ts b/packages/common/test/decorators/bind.decorator.spec.ts index 9aa2decfac7..01552e9c6df 100644 --- a/packages/common/test/decorators/bind.decorator.spec.ts +++ b/packages/common/test/decorators/bind.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { ROUTE_ARGS_METADATA } from '../../constants'; -import { Bind } from '../../decorators/core/bind.decorator'; -import { Req } from '../../decorators/http/route-params.decorator'; +import { ROUTE_ARGS_METADATA } from '../../constants.js'; +import { Bind } from '../../decorators/core/bind.decorator.js'; +import { Req } from '../../decorators/http/route-params.decorator.js'; describe('@Bind', () => { class TestWithMethod { @@ -16,7 +15,7 @@ describe('@Bind', () => { 'test', ); - expect(metadata).to.be.deep.equal({ + expect(metadata).toEqual({ '0:0': { data: undefined, index: 0, diff --git a/packages/common/test/decorators/catch.decorator.spec.ts b/packages/common/test/decorators/catch.decorator.spec.ts index 5fff8ebf1e6..ccf792f0b56 100644 --- a/packages/common/test/decorators/catch.decorator.spec.ts +++ b/packages/common/test/decorators/catch.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { CATCH_WATERMARK, FILTER_CATCH_EXCEPTIONS } from '../../constants'; -import { Catch } from '../../decorators/core/catch.decorator'; +import { CATCH_WATERMARK, FILTER_CATCH_EXCEPTIONS } from '../../constants.js'; +import { Catch } from '../../decorators/core/catch.decorator.js'; describe('@Catch', () => { const exceptions: any = ['exception', 'exception2']; @@ -11,11 +10,11 @@ describe('@Catch', () => { it(`should enhance component with "${CATCH_WATERMARK}" metadata`, () => { const catchWatermark = Reflect.getMetadata(CATCH_WATERMARK, Test); - expect(catchWatermark).to.be.true; + expect(catchWatermark).toBe(true); }); it('should enhance class with expected exceptions array', () => { const metadata = Reflect.getMetadata(FILTER_CATCH_EXCEPTIONS, Test); - expect(metadata).to.be.eql(exceptions); + expect(metadata).toEqual(exceptions); }); }); diff --git a/packages/common/test/decorators/controller.decorator.spec.ts b/packages/common/test/decorators/controller.decorator.spec.ts index 100a4ecbba8..8474384c5e6 100644 --- a/packages/common/test/decorators/controller.decorator.spec.ts +++ b/packages/common/test/decorators/controller.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { VERSION_METADATA, CONTROLLER_WATERMARK } from '../../constants'; -import { Controller } from '../../decorators/core/controller.decorator'; +import { VERSION_METADATA, CONTROLLER_WATERMARK } from '../../constants.js'; +import { Controller } from '../../decorators/core/controller.decorator.js'; describe('@Controller', () => { const reflectedPath = 'test'; @@ -44,27 +43,27 @@ describe('@Controller', () => { EmptyDecorator, ); - expect(controllerWatermark).to.be.true; + expect(controllerWatermark).toBe(true); }); it('should enhance controller with expected path metadata', () => { const path = Reflect.getMetadata('path', Test); - expect(path).to.be.eql(reflectedPath); + expect(path).toEqual(reflectedPath); const path2 = Reflect.getMetadata('path', PathAndHostDecorator); - expect(path2).to.be.eql(reflectedPath); + expect(path2).toEqual(reflectedPath); const path3 = Reflect.getMetadata('path', PathAndHostAndVersionDecorator); - expect(path3).to.be.eql(reflectedPath); + expect(path3).toEqual(reflectedPath); }); it('should enhance controller with expected host metadata', () => { const host = Reflect.getMetadata('host', PathAndHostDecorator); - expect(host).to.be.eql(reflectedHost); + expect(host).toEqual(reflectedHost); const host2 = Reflect.getMetadata('host', HostOnlyDecorator); - expect(host2).to.be.eql(reflectedHost); + expect(host2).toEqual(reflectedHost); const host3 = Reflect.getMetadata('host', PathAndHostArrayDecorator); - expect(host3).to.be.eql(reflectedHostArray); + expect(host3).toEqual(reflectedHostArray); const host4 = Reflect.getMetadata('host', PathAndHostAndVersionDecorator); - expect(host4).to.be.eql(reflectedHost); + expect(host4).toEqual(reflectedHost); }); it('should enhance controller with expected version metadata', () => { @@ -72,39 +71,39 @@ describe('@Controller', () => { VERSION_METADATA, PathAndHostAndVersionDecorator, ); - expect(version).to.be.eql(reflectedVersion); + expect(version).toEqual(reflectedVersion); const version2 = Reflect.getMetadata( VERSION_METADATA, VersionOnlyDecorator, ); - expect(version2).to.be.eql(reflectedVersion); + expect(version2).toEqual(reflectedVersion); const version3 = Reflect.getMetadata( VERSION_METADATA, VersionOnlyArrayDecorator, ); - expect(version3).to.be.eql(reflectedVersionWithoutDuplicates); + expect(version3).toEqual(reflectedVersionWithoutDuplicates); }); it('should set default path when no object passed as param', () => { const path = Reflect.getMetadata('path', EmptyDecorator); - expect(path).to.be.eql('/'); + expect(path).toEqual('/'); const path2 = Reflect.getMetadata('path', HostOnlyDecorator); - expect(path2).to.be.eql('/'); + expect(path2).toEqual('/'); const path3 = Reflect.getMetadata('path', VersionOnlyDecorator); - expect(path3).to.be.eql('/'); + expect(path3).toEqual('/'); }); it('should not set host when no host passed as param', () => { const host = Reflect.getMetadata('host', Test); - expect(host).to.be.undefined; + expect(host).toBeUndefined(); const host2 = Reflect.getMetadata('host', EmptyDecorator); - expect(host2).to.be.undefined; + expect(host2).toBeUndefined(); }); it('should not set version when no version passed as param', () => { const version = Reflect.getMetadata(VERSION_METADATA, Test); - expect(version).to.be.undefined; + expect(version).toBeUndefined(); const version2 = Reflect.getMetadata(VERSION_METADATA, EmptyDecorator); - expect(version2).to.be.undefined; + expect(version2).toBeUndefined(); }); }); diff --git a/packages/common/test/decorators/create-param-decorator.spec.ts b/packages/common/test/decorators/create-param-decorator.spec.ts index 744d655c162..8bf81ff48f7 100644 --- a/packages/common/test/decorators/create-param-decorator.spec.ts +++ b/packages/common/test/decorators/create-param-decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { ROUTE_ARGS_METADATA } from '../../constants'; -import { createParamDecorator } from '../../decorators/http/create-route-param-metadata.decorator'; -import { ParseIntPipe } from '../../index'; +import { ROUTE_ARGS_METADATA } from '../../constants.js'; +import { createParamDecorator } from '../../decorators/http/create-route-param-metadata.decorator.js'; +import { ParseIntPipe } from '../../index.js'; describe('createParamDecorator', () => { let result; @@ -11,7 +10,7 @@ describe('createParamDecorator', () => { result = createParamDecorator(fn); }); it('should return a function as a first element', () => { - expect(result).to.be.a('function'); + expect(result).toBeTypeOf('function'); }); describe('returned decorator', () => { const factoryFn = (data, req) => true; @@ -25,7 +24,7 @@ describe('createParamDecorator', () => { it('should enhance param with "data"', () => { const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); const key = Object.keys(metadata)[0]; - expect(metadata[key]).to.be.eql({ + expect(metadata[key]).toEqual({ data, factory: factoryFn, index: 0, @@ -50,7 +49,7 @@ describe('createParamDecorator', () => { it('should enhance param with "data" and ParseIntPipe', () => { const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); const key = Object.keys(metadata)[0]; - expect(metadata[key]).to.be.eql({ + expect(metadata[key]).toEqual({ data: 'test', factory: factoryFn, index: 0, @@ -65,7 +64,7 @@ describe('createParamDecorator', () => { 'testNoData', ); const key = Object.keys(metadata)[0]; - expect(metadata[key]).to.be.eql({ + expect(metadata[key]).toEqual({ data: undefined, factory: factoryFn, index: 0, @@ -80,7 +79,7 @@ describe('createParamDecorator', () => { 'testNoDataClass', ); const key = Object.keys(metadata)[0]; - expect(metadata[key]).to.be.eql({ + expect(metadata[key]).toEqual({ data: undefined, factory: factoryFn, index: 0, @@ -98,7 +97,143 @@ describe('createParamDecorator', () => { it('should return class type as data parameter', () => { const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); const key = Object.keys(metadata)[0]; - expect(metadata[key].data).to.equal(Data); + expect(metadata[key].data).toBe(Data); + }); + }); + + describe('when options object with schema is passed', () => { + const mockSchema = { + '~standard': { + version: 1, + vendor: 'test', + validate: (v: unknown) => ({ value: v }), + }, + }; + + describe('with data and schema option', () => { + const data = 'testData'; + class Test { + public test( + @Decorator(data, { schema: mockSchema }) + param, + ) {} + } + it('should enhance param with "data" and "schema"', () => { + const metadata = Reflect.getMetadata( + ROUTE_ARGS_METADATA, + Test, + 'test', + ); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + data: 'testData', + factory: factoryFn, + index: 0, + pipes: [], + schema: mockSchema, + }); + }); + }); + + describe('with schema option only (no data)', () => { + class Test { + public test( + @Decorator({ schema: mockSchema }) + param, + ) {} + } + it('should enhance param with schema and no data', () => { + const metadata = Reflect.getMetadata( + ROUTE_ARGS_METADATA, + Test, + 'test', + ); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + data: undefined, + factory: factoryFn, + index: 0, + pipes: [], + schema: mockSchema, + }); + }); + }); + + describe('with data, pipes, and schema option', () => { + const data = 'testData'; + const pipe = new ParseIntPipe(); + class Test { + public test( + @Decorator(data, pipe, { schema: mockSchema }) + param, + ) {} + } + it('should enhance param with "data", pipes, and schema', () => { + const metadata = Reflect.getMetadata( + ROUTE_ARGS_METADATA, + Test, + 'test', + ); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + data: 'testData', + factory: factoryFn, + index: 0, + pipes: [pipe], + schema: mockSchema, + }); + }); + }); + + describe('with pipes in options object', () => { + const data = 'testData'; + const pipe = new ParseIntPipe(); + class Test { + public test( + @Decorator(data, { schema: mockSchema, pipes: [pipe] }) + param, + ) {} + } + it('should enhance param with "data", pipes from options, and schema', () => { + const metadata = Reflect.getMetadata( + ROUTE_ARGS_METADATA, + Test, + 'test', + ); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + data: 'testData', + factory: factoryFn, + index: 0, + pipes: [pipe], + schema: mockSchema, + }); + }); + }); + + describe('with options containing only pipes (no schema)', () => { + const data = 'testData'; + const pipe = new ParseIntPipe(); + class Test { + public test( + @Decorator(data, { pipes: [pipe] }) + param, + ) {} + } + it('should enhance param with "data" and pipes from options', () => { + const metadata = Reflect.getMetadata( + ROUTE_ARGS_METADATA, + Test, + 'test', + ); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + data: 'testData', + factory: factoryFn, + index: 0, + pipes: [pipe], + }); + }); }); }); }); @@ -126,7 +261,7 @@ describe('createParamDecorator', () => { it('should enhance param with "data" as string', () => { const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); const key = Object.keys(metadata)[0]; - expect(metadata[key]).to.be.eql({ + expect(metadata[key]).toEqual({ data: 'test', factory: factoryFn, index: 0, @@ -146,7 +281,7 @@ describe('createParamDecorator', () => { it('should enhance param with "data" as number', () => { const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); const key = Object.keys(metadata)[0]; - expect(metadata[key]).to.be.eql({ + expect(metadata[key]).toEqual({ data: 10, factory: factoryFn, index: 0, @@ -165,7 +300,7 @@ describe('createParamDecorator', () => { it('should enhance param with "data" as custom Type', () => { const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); const key = Object.keys(metadata)[0]; - expect(metadata[key]).to.be.eql({ + expect(metadata[key]).toEqual({ data: { name: 'john' }, factory: factoryFn, index: 0, diff --git a/packages/common/test/decorators/dependencies.decorator.spec.ts b/packages/common/test/decorators/dependencies.decorator.spec.ts index 94a8b08a0d3..0aebffc3ff4 100644 --- a/packages/common/test/decorators/dependencies.decorator.spec.ts +++ b/packages/common/test/decorators/dependencies.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { Dependencies } from '../../decorators/core/dependencies.decorator'; -import { PARAMTYPES_METADATA } from '../../constants'; +import { Dependencies } from '../../decorators/core/dependencies.decorator.js'; +import { PARAMTYPES_METADATA } from '../../constants.js'; describe('@Dependencies', () => { const dep = 'test', @@ -14,11 +13,11 @@ describe('@Dependencies', () => { it('should enhance class with expected dependencies array', () => { const metadata = Reflect.getMetadata(PARAMTYPES_METADATA, Test); - expect(metadata).to.be.eql(deps); + expect(metadata).toEqual(deps); }); it('should makes passed array flatten', () => { const metadata = Reflect.getMetadata(PARAMTYPES_METADATA, Test2); - expect(metadata).to.be.eql([dep, dep2]); + expect(metadata).toEqual([dep, dep2]); }); }); diff --git a/packages/common/test/decorators/exception-filters.decorator.spec.ts b/packages/common/test/decorators/exception-filters.decorator.spec.ts index 36d0b7c95c6..24817a7c1c8 100644 --- a/packages/common/test/decorators/exception-filters.decorator.spec.ts +++ b/packages/common/test/decorators/exception-filters.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { EXCEPTION_FILTERS_METADATA } from '../../constants'; -import { UseFilters } from '../../decorators/core/exception-filters.decorator'; -import { InvalidDecoratorItemException } from '../../utils/validate-each.util'; +import { EXCEPTION_FILTERS_METADATA } from '../../constants.js'; +import { UseFilters } from '../../decorators/core/exception-filters.decorator.js'; +import { InvalidDecoratorItemException } from '../../utils/validate-each.util.js'; class Filter { catch() {} @@ -20,7 +19,7 @@ describe('@UseFilters', () => { it('should enhance class with expected exception filters array', () => { const metadata = Reflect.getMetadata(EXCEPTION_FILTERS_METADATA, Test); - expect(metadata).to.be.eql(filters); + expect(metadata).toEqual(filters); }); it('should enhance method with expected exception filters array', () => { @@ -28,14 +27,14 @@ describe('@UseFilters', () => { EXCEPTION_FILTERS_METADATA, TestWithMethod.test, ); - expect(metadata).to.be.eql(filters); + expect(metadata).toEqual(filters); }); it('when object is invalid should throw exception', () => { try { UseFilters('test' as any)(() => {}); } catch (e) { - expect(e).to.be.instanceof(InvalidDecoratorItemException); + expect(e).toBeInstanceOf(InvalidDecoratorItemException); } }); }); diff --git a/packages/common/test/decorators/global.decorator.spec.ts b/packages/common/test/decorators/global.decorator.spec.ts index 8eac6bb0612..434cb40b0d6 100644 --- a/packages/common/test/decorators/global.decorator.spec.ts +++ b/packages/common/test/decorators/global.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { GLOBAL_MODULE_METADATA } from '../../constants'; -import { Global } from '../../index'; +import { GLOBAL_MODULE_METADATA } from '../../constants.js'; +import { Global } from '../../index.js'; describe('@Global', () => { @Global() @@ -8,6 +7,6 @@ describe('@Global', () => { it('should enrich metatype with GlobalModule metadata', () => { const isGlobal = Reflect.getMetadata(GLOBAL_MODULE_METADATA, Test); - expect(isGlobal).to.be.true; + expect(isGlobal).toBe(true); }); }); diff --git a/packages/common/test/decorators/header.decorator.spec.ts b/packages/common/test/decorators/header.decorator.spec.ts index 9da461d051b..3e501d7cd5b 100644 --- a/packages/common/test/decorators/header.decorator.spec.ts +++ b/packages/common/test/decorators/header.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { Header } from '../../decorators/http'; -import { HEADERS_METADATA } from '../../constants'; +import { Header } from '../../decorators/http/index.js'; +import { HEADERS_METADATA } from '../../constants.js'; describe('@Header', () => { class Test { @@ -11,7 +10,7 @@ describe('@Header', () => { it('should enhance method with expected template string', () => { const metadata = Reflect.getMetadata(HEADERS_METADATA, Test.test); - expect(metadata).to.be.eql([ + expect(metadata).toEqual([ { name: 'Authorization', value: 'JWT' }, { name: 'Content-Type', value: 'Test' }, ]); diff --git a/packages/common/test/decorators/http-code.decorator.spec.ts b/packages/common/test/decorators/http-code.decorator.spec.ts index 498fd5afe9d..24690582008 100644 --- a/packages/common/test/decorators/http-code.decorator.spec.ts +++ b/packages/common/test/decorators/http-code.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { HttpCode } from '../../decorators/http/http-code.decorator'; -import { HTTP_CODE_METADATA } from '../../constants'; +import { HttpCode } from '../../decorators/http/http-code.decorator.js'; +import { HTTP_CODE_METADATA } from '../../constants.js'; describe('@HttpCode', () => { const httpCode = 200; @@ -11,6 +10,6 @@ describe('@HttpCode', () => { it('should enhance method with expected http status code', () => { const metadata = Reflect.getMetadata(HTTP_CODE_METADATA, Test.test); - expect(metadata).to.be.eql(httpCode); + expect(metadata).toEqual(httpCode); }); }); diff --git a/packages/common/test/decorators/inject.decorator.spec.ts b/packages/common/test/decorators/inject.decorator.spec.ts index 5c40c25352b..e32661b7218 100644 --- a/packages/common/test/decorators/inject.decorator.spec.ts +++ b/packages/common/test/decorators/inject.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { SELF_DECLARED_DEPS_METADATA } from '../../constants'; -import { Inject } from '../../index'; +import { SELF_DECLARED_DEPS_METADATA } from '../../constants.js'; +import { Inject } from '../../index.js'; describe('@Inject', () => { const opaqueToken = () => ({}); @@ -20,6 +19,6 @@ describe('@Inject', () => { { index: 1, param: 'test2' }, { index: 0, param: 'test' }, ]; - expect(metadata).to.be.eql(expectedMetadata); + expect(metadata).toEqual(expectedMetadata); }); }); diff --git a/packages/common/test/decorators/injectable.decorator.spec.ts b/packages/common/test/decorators/injectable.decorator.spec.ts index 388ecd71011..564982a2b34 100644 --- a/packages/common/test/decorators/injectable.decorator.spec.ts +++ b/packages/common/test/decorators/injectable.decorator.spec.ts @@ -1,6 +1,8 @@ -import { expect } from 'chai'; -import { SCOPE_OPTIONS_METADATA, INJECTABLE_WATERMARK } from '../../constants'; -import { Injectable, mixin } from '../../index'; +import { + SCOPE_OPTIONS_METADATA, + INJECTABLE_WATERMARK, +} from '../../constants.js'; +import { Injectable, mixin } from '../../index.js'; describe('@Injectable', () => { const options = {}; @@ -16,7 +18,7 @@ describe('@Injectable', () => { TestMiddleware, ); - expect(injectableWatermark).to.be.eql(true); + expect(injectableWatermark).toEqual(true); }); it('should enhance component with "design:paramtypes" metadata', () => { @@ -25,8 +27,8 @@ describe('@Injectable', () => { TestMiddleware, ); - expect(constructorParams[0]).to.be.eql(Number); - expect(constructorParams[1]).to.be.eql(String); + expect(constructorParams[0]).toEqual(Number); + expect(constructorParams[1]).toEqual(String); }); it(`should enhance component with "${SCOPE_OPTIONS_METADATA}" metadata`, () => { @@ -35,7 +37,7 @@ describe('@Injectable', () => { TestMiddleware, ); - expect(constructorParams).to.be.eql(options); + expect(constructorParams).toEqual(options); }); }); @@ -48,14 +50,14 @@ describe('mixin', () => { it('should set name of metatype', () => { const type = mixin(Test); - expect(type.name).to.not.eql('Test'); + expect(type.name).not.toEqual('Test'); }); it('should not lost the design:paramtypes metadata', () => { const type = mixin(Test); const constructorParams = Reflect.getMetadata('design:paramtypes', type); - expect(constructorParams[0]).to.be.eql(Number); - expect(constructorParams[1]).to.be.eql(String); + expect(constructorParams[0]).toEqual(Number); + expect(constructorParams[1]).toEqual(String); }); }); diff --git a/packages/common/test/decorators/module.decorator.spec.ts b/packages/common/test/decorators/module.decorator.spec.ts index dafebe5cd4b..8b6034a48f3 100644 --- a/packages/common/test/decorators/module.decorator.spec.ts +++ b/packages/common/test/decorators/module.decorator.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { Module } from '../../decorators/modules/module.decorator'; +import { Module } from '../../decorators/modules/module.decorator.js'; describe('@Module', () => { const moduleProps = { @@ -18,10 +17,10 @@ describe('@Module', () => { const exports = Reflect.getMetadata('exports', TestModule); const controllers = Reflect.getMetadata('controllers', TestModule); - expect(imports).to.be.eql(moduleProps.imports); - expect(providers).to.be.eql(moduleProps.providers); - expect(controllers).to.be.eql(moduleProps.controllers); - expect(exports).to.be.eql(moduleProps.exports); + expect(imports).toEqual(moduleProps.imports); + expect(providers).toEqual(moduleProps.providers); + expect(controllers).toEqual(moduleProps.controllers); + expect(exports).toEqual(moduleProps.exports); }); it('should throw exception when module properties are invalid', () => { @@ -30,6 +29,6 @@ describe('@Module', () => { test: [], }; - expect(Module.bind(null, invalidProps)).to.throw(Error); + expect(Module.bind(null, invalidProps)).toThrow(Error); }); }); diff --git a/packages/common/test/decorators/redirect.decorator.spec.ts b/packages/common/test/decorators/redirect.decorator.spec.ts index 358cef45e51..2eb71ec8232 100644 --- a/packages/common/test/decorators/redirect.decorator.spec.ts +++ b/packages/common/test/decorators/redirect.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { REDIRECT_METADATA } from '../../constants'; -import { Redirect } from '../../decorators/http/redirect.decorator'; -import { HttpStatus } from '../../index'; +import { REDIRECT_METADATA } from '../../constants.js'; +import { Redirect } from '../../decorators/http/redirect.decorator.js'; +import { HttpStatus } from '../../index.js'; describe('@Redirect', () => { const url = 'http://test.com'; @@ -14,11 +13,11 @@ describe('@Redirect', () => { it('should enhance method with expected redirect url string', () => { const metadata = Reflect.getMetadata(REDIRECT_METADATA, Test.test); - expect(metadata.url).to.be.eql(url); + expect(metadata.url).toEqual(url); }); it('should enhance method with expected response code', () => { const metadata = Reflect.getMetadata(REDIRECT_METADATA, Test.test); - expect(metadata.statusCode).to.be.eql(statusCode); + expect(metadata.statusCode).toEqual(statusCode); }); }); diff --git a/packages/common/test/decorators/render.decorator.spec.ts b/packages/common/test/decorators/render.decorator.spec.ts index 5ea573568e1..2f6a6cb77e0 100644 --- a/packages/common/test/decorators/render.decorator.spec.ts +++ b/packages/common/test/decorators/render.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { Render } from '../../decorators/http/render.decorator'; -import { RENDER_METADATA } from '../../constants'; +import { Render } from '../../decorators/http/render.decorator.js'; +import { RENDER_METADATA } from '../../constants.js'; describe('@Render', () => { const template = 'template'; @@ -12,6 +11,6 @@ describe('@Render', () => { it('should enhance method with expected template string', () => { const metadata = Reflect.getMetadata(RENDER_METADATA, Test.test); - expect(metadata).to.be.eql(template); + expect(metadata).toEqual(template); }); }); diff --git a/packages/common/test/decorators/request-mapping.decorator.spec.ts b/packages/common/test/decorators/request-mapping.decorator.spec.ts index 867c24995d6..cb4b47117d9 100644 --- a/packages/common/test/decorators/request-mapping.decorator.spec.ts +++ b/packages/common/test/decorators/request-mapping.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { RequestMapping } from '../../decorators/http/request-mapping.decorator'; -import { RequestMethod } from '../../enums/request-method.enum'; +import { RequestMapping } from '../../decorators/http/request-mapping.decorator.js'; +import { RequestMethod } from '../../enums/request-method.enum.js'; describe('@RequestMapping', () => { const requestProps = { @@ -27,10 +26,10 @@ describe('@RequestMapping', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestProps.path); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPropsUsingArray.path); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestProps.path); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPropsUsingArray.path); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set request method on GET by default', () => { @@ -41,7 +40,7 @@ describe('@RequestMapping', () => { const method = Reflect.getMetadata('method', Test.test); - expect(method).to.be.eql(RequestMethod.GET); + expect(method).toEqual(RequestMethod.GET); }); it('should set path on "/" by default', () => { @@ -56,7 +55,7 @@ describe('@RequestMapping', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); diff --git a/packages/common/test/decorators/route-params.decorator.spec.ts b/packages/common/test/decorators/route-params.decorator.spec.ts index da3a85a9adb..13e45e273d0 100644 --- a/packages/common/test/decorators/route-params.decorator.spec.ts +++ b/packages/common/test/decorators/route-params.decorator.spec.ts @@ -1,24 +1,29 @@ -import { expect } from 'chai'; -import { Body, HostParam, Param, Query, Search } from '../../decorators'; -import { RequestMethod } from '../../enums/request-method.enum'; +import { ROUTE_ARGS_METADATA } from '../../constants.js'; +import { + Body, + HostParam, + Param, + Query, + Search, +} from '../../decorators/index.js'; +import { RequestMethod } from '../../enums/request-method.enum.js'; +import { RouteParamtypes } from '../../enums/route-paramtypes.enum.js'; import { All, + Copy, Delete, Get, + Lock, + Mkcol, + Move, ParseIntPipe, Patch, Post, - Put, Propfind, Proppatch, - Mkcol, - Move, - Copy, - Lock, + Put, Unlock, -} from '../../index'; -import { ROUTE_ARGS_METADATA } from '../../constants'; -import { RouteParamtypes } from '../../enums/route-paramtypes.enum'; +} from '../../index.js'; describe('@Get', () => { const requestPath = 'test'; @@ -52,15 +57,15 @@ describe('@Get', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(args[`${RouteParamtypes.PARAM}:0`]).to.be.eql({ + expect(path).toEqual(requestPath); + expect(args[`${RouteParamtypes.PARAM}:0`]).toEqual({ index: 0, data: 'id', pipes: [ParseIntPipe], }); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -75,8 +80,8 @@ describe('@Get', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -107,10 +112,10 @@ describe('@Post', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -133,8 +138,8 @@ describe('@Post', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -165,10 +170,10 @@ describe('@Delete', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -183,8 +188,8 @@ describe('@Delete', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -215,10 +220,10 @@ describe('@All', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -233,8 +238,8 @@ describe('@All', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -265,10 +270,10 @@ describe('@Put', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -283,8 +288,8 @@ describe('@Put', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -315,10 +320,10 @@ describe('@Patch', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -333,8 +338,8 @@ describe('@Patch', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -365,10 +370,10 @@ describe('@Search', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -390,8 +395,8 @@ describe('@Search', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -424,10 +429,10 @@ describe('Inheritance', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); }); @@ -458,10 +463,10 @@ describe('@PropFind', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -484,8 +489,8 @@ describe('@PropFind', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -516,10 +521,10 @@ describe('@PropPatch', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -542,8 +547,8 @@ describe('@PropPatch', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -574,10 +579,10 @@ describe('@MkCol', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -600,8 +605,8 @@ describe('@MkCol', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -632,10 +637,10 @@ describe('@Copy', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -658,8 +663,8 @@ describe('@Copy', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -690,10 +695,10 @@ describe('@Move', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -716,8 +721,8 @@ describe('@Move', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -748,10 +753,10 @@ describe('@Lock', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -774,8 +779,8 @@ describe('@Lock', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); }); }); @@ -806,10 +811,10 @@ describe('@Unlock', () => { const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); const methodUsingArray = Reflect.getMetadata('method', Test.testUsingArray); - expect(path).to.be.eql(requestPath); - expect(method).to.be.eql(requestProps.method); - expect(pathUsingArray).to.be.eql(requestPathUsingArray); - expect(methodUsingArray).to.be.eql(requestPropsUsingArray.method); + expect(path).toEqual(requestPath); + expect(method).toEqual(requestProps.method); + expect(pathUsingArray).toEqual(requestPathUsingArray); + expect(methodUsingArray).toEqual(requestPropsUsingArray.method); }); it('should set path on "/" by default', () => { @@ -832,7 +837,200 @@ describe('@Unlock', () => { const path = Reflect.getMetadata('path', Test.test); const pathUsingArray = Reflect.getMetadata('path', Test.testUsingArray); - expect(path).to.be.eql('/'); - expect(pathUsingArray).to.be.eql('/'); + expect(path).toEqual('/'); + expect(pathUsingArray).toEqual('/'); + }); +}); + +describe('@Body with ParameterDecoratorOptions', () => { + const mockSchema = { + '~standard': { + version: 1 as const, + vendor: 'test', + validate: (v: unknown) => ({ value: v }), + }, + }; + + it('should enhance param with schema when options passed as the only argument', () => { + class Test { + public test(@Body({ schema: mockSchema }) body) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [], + schema: mockSchema, + }); + }); + + it('should enhance param with pipes when options with pipes passed as the only argument', () => { + class Test { + public test(@Body({ schema: mockSchema, pipes: [ParseIntPipe] }) body) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [ParseIntPipe], + schema: mockSchema, + }); + }); + + it('should enhance param with schema when options passed as second argument with property', () => { + class Test { + public test(@Body('role', { schema: mockSchema }) body) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: 'role', + pipes: [], + schema: mockSchema, + }); + }); + + it('should not confuse a pipe instance with options', () => { + class Test { + public test(@Body(new ParseIntPipe()) body) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key].data).toBeUndefined(); + expect(metadata[key].pipes).toHaveLength(1); + expect(metadata[key].schema).toBeUndefined(); + }); +}); + +describe('@Query with ParameterDecoratorOptions', () => { + const mockSchema = { + '~standard': { + version: 1 as const, + vendor: 'test', + validate: (v: unknown) => ({ value: v }), + }, + }; + + it('should enhance param with schema when options passed as the only argument', () => { + class Test { + public test(@Query({ schema: mockSchema }) query) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [], + schema: mockSchema, + }); + }); + + it('should enhance param with pipes when options with pipes passed as the only argument', () => { + class Test { + public test( + @Query({ schema: mockSchema, pipes: [ParseIntPipe] }) query, + ) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [ParseIntPipe], + schema: mockSchema, + }); + }); + + it('should enhance param with schema when options passed as second argument with property', () => { + class Test { + public test(@Query('user', { schema: mockSchema }) user) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: 'user', + pipes: [], + schema: mockSchema, + }); + }); + + it('should not confuse a pipe instance with options', () => { + class Test { + public test(@Query(new ParseIntPipe()) query) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key].data).toBeUndefined(); + expect(metadata[key].pipes).toHaveLength(1); + expect(metadata[key].schema).toBeUndefined(); + }); +}); + +describe('@Param with ParameterDecoratorOptions', () => { + const mockSchema = { + '~standard': { + version: 1 as const, + vendor: 'test', + validate: (v: unknown) => ({ value: v }), + }, + }; + + it('should enhance param with schema when options passed as the only argument', () => { + class Test { + public test(@Param({ schema: mockSchema }) params) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [], + schema: mockSchema, + }); + }); + + it('should enhance param with pipes when options with pipes passed as the only argument', () => { + class Test { + public test( + @Param({ schema: mockSchema, pipes: [ParseIntPipe] }) params, + ) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [ParseIntPipe], + schema: mockSchema, + }); + }); + + it('should enhance param with schema when options passed as second argument with property', () => { + class Test { + public test(@Param('id', { schema: mockSchema }) id) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: 'id', + pipes: [], + schema: mockSchema, + }); + }); + + it('should not confuse a pipe instance with options', () => { + class Test { + public test(@Param(new ParseIntPipe()) params) {} + } + const metadata = Reflect.getMetadata(ROUTE_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key].data).toBeUndefined(); + expect(metadata[key].pipes).toHaveLength(1); + expect(metadata[key].schema).toBeUndefined(); }); }); diff --git a/packages/common/test/decorators/set-metadata.decorator.spec.ts b/packages/common/test/decorators/set-metadata.decorator.spec.ts index 8fe1cd98194..8f1e19a8dae 100644 --- a/packages/common/test/decorators/set-metadata.decorator.spec.ts +++ b/packages/common/test/decorators/set-metadata.decorator.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { SetMetadata } from '../../decorators/core/set-metadata.decorator'; +import { SetMetadata } from '../../decorators/core/set-metadata.decorator.js'; describe('@SetMetadata', () => { const key = 'key', @@ -15,11 +14,11 @@ describe('@SetMetadata', () => { it('should enhance class with expected metadata', () => { const metadata = Reflect.getMetadata(key, Test); - expect(metadata).to.be.eql(value); + expect(metadata).toEqual(value); }); it('should enhance method with expected metadata', () => { const metadata = Reflect.getMetadata(key, TestWithMethod.test); - expect(metadata).to.be.eql(value); + expect(metadata).toEqual(value); }); }); diff --git a/packages/common/test/decorators/sse.decorator.spec.ts b/packages/common/test/decorators/sse.decorator.spec.ts index 0d40e225dba..040a17931aa 100644 --- a/packages/common/test/decorators/sse.decorator.spec.ts +++ b/packages/common/test/decorators/sse.decorator.spec.ts @@ -1,7 +1,10 @@ -import { expect } from 'chai'; -import { METHOD_METADATA, PATH_METADATA, SSE_METADATA } from '../../constants'; -import { Sse } from '../../decorators/http/sse.decorator'; -import { RequestMethod } from '../../enums/request-method.enum'; +import { + METHOD_METADATA, + PATH_METADATA, + SSE_METADATA, +} from '../../constants.js'; +import { Sse } from '../../decorators/http/sse.decorator.js'; +import { RequestMethod } from '../../enums/request-method.enum.js'; describe('@Sse', () => { const prefix = '/prefix'; @@ -15,22 +18,22 @@ describe('@Sse', () => { it('should enhance method with expected http status code', () => { const path = Reflect.getMetadata(PATH_METADATA, Test.test); - expect(path).to.be.eql('/prefix'); + expect(path).toEqual('/prefix'); const method = Reflect.getMetadata(METHOD_METADATA, Test.test); - expect(method).to.be.eql(RequestMethod.GET); + expect(method).toEqual(RequestMethod.GET); const metadata = Reflect.getMetadata(SSE_METADATA, Test.test); - expect(metadata).to.be.eql(true); + expect(metadata).toEqual(true); }); it('should enhance method with expected http status code and method from options', () => { const path = Reflect.getMetadata(PATH_METADATA, Test.testUsingOptions); - expect(path).to.be.eql('/prefix'); + expect(path).toEqual('/prefix'); const method = Reflect.getMetadata(METHOD_METADATA, Test.testUsingOptions); - expect(method).to.be.eql(RequestMethod.POST); + expect(method).toEqual(RequestMethod.POST); const metadata = Reflect.getMetadata(SSE_METADATA, Test.testUsingOptions); - expect(metadata).to.be.eql(true); + expect(metadata).toEqual(true); }); }); diff --git a/packages/common/test/decorators/use-guards.decorator.spec.ts b/packages/common/test/decorators/use-guards.decorator.spec.ts index dd822cec699..534cd680037 100644 --- a/packages/common/test/decorators/use-guards.decorator.spec.ts +++ b/packages/common/test/decorators/use-guards.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { GUARDS_METADATA } from '../../constants'; -import { UseGuards } from '../../decorators/core/use-guards.decorator'; -import { InvalidDecoratorItemException } from '../../utils/validate-each.util'; +import { GUARDS_METADATA } from '../../constants.js'; +import { UseGuards } from '../../decorators/core/use-guards.decorator.js'; +import { InvalidDecoratorItemException } from '../../utils/validate-each.util.js'; class Guard {} @@ -24,24 +23,24 @@ describe('@UseGuards', () => { it('should enhance class with expected guards array', () => { const metadata = Reflect.getMetadata(GUARDS_METADATA, Test); - expect(metadata).to.be.eql(guards); + expect(metadata).toEqual(guards); }); it('should enhance method with expected guards array', () => { const metadata = Reflect.getMetadata(GUARDS_METADATA, TestWithMethod.test); - expect(metadata).to.be.eql(guards); + expect(metadata).toEqual(guards); }); it('should enhance method with multiple guards array', () => { const metadata = Reflect.getMetadata(GUARDS_METADATA, Test2.test); - expect(metadata).to.be.eql(guards.concat(guards)); + expect(metadata).toEqual(guards.concat(guards)); }); it('should throw exception when object is invalid', () => { try { UseGuards('test' as any)(() => {}); } catch (e) { - expect(e).to.be.instanceof(InvalidDecoratorItemException); + expect(e).toBeInstanceOf(InvalidDecoratorItemException); } }); }); diff --git a/packages/common/test/decorators/use-interceptors.decorator.spec.ts b/packages/common/test/decorators/use-interceptors.decorator.spec.ts index 48449e05bc8..e7e7d9b7562 100644 --- a/packages/common/test/decorators/use-interceptors.decorator.spec.ts +++ b/packages/common/test/decorators/use-interceptors.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { INTERCEPTORS_METADATA } from '../../constants'; -import { UseInterceptors } from '../../decorators/core/use-interceptors.decorator'; -import { InvalidDecoratorItemException } from '../../utils/validate-each.util'; +import { INTERCEPTORS_METADATA } from '../../constants.js'; +import { UseInterceptors } from '../../decorators/core/use-interceptors.decorator.js'; +import { InvalidDecoratorItemException } from '../../utils/validate-each.util.js'; class Interceptor {} @@ -18,7 +17,7 @@ describe('@UseInterceptors', () => { it('should enhance class with expected interceptors array', () => { const metadata = Reflect.getMetadata(INTERCEPTORS_METADATA, Test); - expect(metadata).to.be.eql(interceptors); + expect(metadata).toEqual(interceptors); }); it('should enhance method with expected interceptors array', () => { @@ -26,7 +25,7 @@ describe('@UseInterceptors', () => { INTERCEPTORS_METADATA, TestWithMethod.test, ); - expect(metadata).to.be.eql(interceptors); + expect(metadata).toEqual(interceptors); }); it('when object is invalid should throw exception', () => { @@ -36,7 +35,7 @@ describe('@UseInterceptors', () => { } catch (e) { error = e; } - expect(error).to.be.instanceof(InvalidDecoratorItemException); + expect(error).toBeInstanceOf(InvalidDecoratorItemException); }); it('when object is valid should not throw exception', () => { @@ -50,6 +49,6 @@ describe('@UseInterceptors', () => { } catch (e) { error = e; } - expect(error).to.be.undefined; + expect(error).toBeUndefined(); }); }); diff --git a/packages/common/test/decorators/use-pipes.decorator.spec.ts b/packages/common/test/decorators/use-pipes.decorator.spec.ts index 63db0345dd2..2fbaf5dc329 100644 --- a/packages/common/test/decorators/use-pipes.decorator.spec.ts +++ b/packages/common/test/decorators/use-pipes.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { PIPES_METADATA } from '../../constants'; -import { UsePipes } from '../../decorators/core/use-pipes.decorator'; -import { InvalidDecoratorItemException } from '../../utils/validate-each.util'; +import { PIPES_METADATA } from '../../constants.js'; +import { UsePipes } from '../../decorators/core/use-pipes.decorator.js'; +import { InvalidDecoratorItemException } from '../../utils/validate-each.util.js'; class Pipe { transform() {} @@ -20,19 +19,19 @@ describe('@UsePipes', () => { it('should enhance class with expected pipes array', () => { const metadata = Reflect.getMetadata(PIPES_METADATA, Test); - expect(metadata).to.be.eql(pipes); + expect(metadata).toEqual(pipes); }); it('should enhance method with expected pipes array', () => { const metadata = Reflect.getMetadata(PIPES_METADATA, TestWithMethod.test); - expect(metadata).to.be.eql(pipes); + expect(metadata).toEqual(pipes); }); it('when object is invalid should throw exception', () => { try { UsePipes('test' as any)(() => {}); } catch (e) { - expect(e).to.be.instanceof(InvalidDecoratorItemException); + expect(e).toBeInstanceOf(InvalidDecoratorItemException); } }); }); diff --git a/packages/common/test/decorators/version.decorator.spec.ts b/packages/common/test/decorators/version.decorator.spec.ts index d33ea668da8..c7984e467da 100644 --- a/packages/common/test/decorators/version.decorator.spec.ts +++ b/packages/common/test/decorators/version.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { VERSION_METADATA } from '../../constants'; -import { Version } from '../../decorators/core/version.decorator'; +import { VERSION_METADATA } from '../../constants.js'; +import { Version } from '../../decorators/core/version.decorator.js'; describe('@Version', () => { const version = '1'; @@ -17,7 +16,7 @@ describe('@Version', () => { it('should enhance method with expected version string', () => { const metadata = Reflect.getMetadata(VERSION_METADATA, Test.oneVersion); - expect(metadata).to.be.eql(version); + expect(metadata).toEqual(version); }); it('should enhance method with expected version array', () => { @@ -25,6 +24,6 @@ describe('@Version', () => { VERSION_METADATA, Test.multipleVersions, ); - expect(metadata).to.be.eql(versionsWithoutDuplicates); + expect(metadata).toEqual(versionsWithoutDuplicates); }); }); diff --git a/packages/common/test/exceptions/http.exception.spec.ts b/packages/common/test/exceptions/http.exception.spec.ts index b9aa2aa3bd9..c1abf38fdcf 100644 --- a/packages/common/test/exceptions/http.exception.spec.ts +++ b/packages/common/test/exceptions/http.exception.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { Type } from '../../../common'; +import { Type } from '../../../common/index.js'; import { BadGatewayException, BadRequestException, @@ -23,13 +22,14 @@ import { UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, -} from '../../exceptions'; +} from '../../exceptions/index.js'; +import { HttpStatus } from '@nestjs/common'; describe('HttpException', () => { describe('getResponse', () => { it('should return a response as a string when input is a string', () => { const message = 'My error message'; - expect(new HttpException(message, 404).getResponse()).to.be.eql( + expect(new HttpException(message, 404).getResponse()).toEqual( 'My error message', ); }); @@ -40,12 +40,12 @@ describe('HttpException', () => { reason: 'this can be a human readable reason', anything: 'else', }; - expect(new HttpException(message, 404).getResponse()).to.be.eql(message); + expect(new HttpException(message, 404).getResponse()).toEqual(message); }); it('should return a message from a built-in exception as an object', () => { const message = 'My error message'; - expect(new BadRequestException(message).getResponse()).to.be.eql({ + expect(new BadRequestException(message).getResponse()).toEqual({ statusCode: 400, error: 'Bad Request', message: 'My error message', @@ -53,7 +53,7 @@ describe('HttpException', () => { }); it('should return an object even when the message is undefined', () => { - expect(new BadRequestException().getResponse()).to.be.eql({ + expect(new BadRequestException().getResponse()).toEqual({ statusCode: 400, message: 'Bad Request', }); @@ -88,7 +88,7 @@ describe('HttpException', () => { ]; testCases.forEach(([ExceptionClass, expectedStatus]) => { - expect(new ExceptionClass().getStatus()).to.be.eql(expectedStatus); + expect(new ExceptionClass().getStatus()).toEqual(expectedStatus); }); }); }); @@ -121,7 +121,7 @@ describe('HttpException', () => { testCases.forEach( ([ExceptionClass, expectedStatus, expectedMessage]) => { - expect(new ExceptionClass().getResponse()).to.be.eql({ + expect(new ExceptionClass().getResponse()).toEqual({ message: expectedMessage, statusCode: expectedStatus, }); @@ -134,7 +134,7 @@ describe('HttpException', () => { description: 'Some error description', }); - expect(badRequestError.getResponse()).to.be.eql({ + expect(badRequestError.getResponse()).toEqual({ message: 'ErrorMessage', error: 'Some error description', statusCode: 400, @@ -145,7 +145,7 @@ describe('HttpException', () => { it('should inherit from error', () => { const error = new HttpException('', 400); - expect(error instanceof Error).to.be.true; + expect(error instanceof Error).toBe(true); }); describe('when serializing', () => { @@ -153,8 +153,8 @@ describe('HttpException', () => { it('should concatenate HttpException with the given message', () => { const responseAsString = 'Some Error'; const error = new HttpException(responseAsString, 400); - expect(`${error}`).to.be.eql(`HttpException: ${responseAsString}`); - expect(`${error}`.includes('[object Object]')).to.not.be.true; + expect(`${error}`).toEqual(`HttpException: ${responseAsString}`); + expect(`${error}`.includes('[object Object]')).not.toBe(true); }); }); @@ -164,12 +164,12 @@ describe('HttpException', () => { const error = new HttpException(responseAsObject, 400); const badRequestError = new BadRequestException(responseAsObject); - expect(`${error}`).to.be.eql(`HttpException: Http Exception`); - expect(`${badRequestError}`).to.be.eql( + expect(`${error}`).toEqual(`HttpException: Http Exception`); + expect(`${badRequestError}`).toEqual( `BadRequestException: Bad Request Exception`, ); - expect(`${error}`.includes('[object Object]')).to.not.be.true; - expect(`${badRequestError}`.includes('[object Object]')).to.not.be.true; + expect(`${error}`.includes('[object Object]')).not.toBe(true); + expect(`${badRequestError}`.includes('[object Object]')).not.toBe(true); }); }); }); @@ -180,7 +180,7 @@ describe('HttpException', () => { const object = { message: 'test', }; - expect(HttpException.createBody(object)).to.be.eql(object); + expect(HttpException.createBody(object)).toEqual(object); }); }); describe('when string has been passed', () => { @@ -188,7 +188,7 @@ describe('HttpException', () => { const error = 'test'; const status = 500; const message = 'error'; - expect(HttpException.createBody(message, error, status)).to.be.eql({ + expect(HttpException.createBody(message, error, status)).toEqual({ error, message, statusCode: status, @@ -199,7 +199,7 @@ describe('HttpException', () => { it('should return expected object', () => { const status = 500; const error = 'error'; - expect(HttpException.createBody(null, error, status)).to.be.eql({ + expect(HttpException.createBody(null, error, status)).toEqual({ message: error, statusCode: status, }); @@ -208,7 +208,7 @@ describe('HttpException', () => { it('should not override pre-defined body if message is array', () => { expect( HttpException.createBody(['a', 'random', 'array'], 'error', 200), - ).to.eql({ + ).toEqual({ message: ['a', 'random', 'array'], error: 'error', statusCode: 200, @@ -225,10 +225,10 @@ describe('HttpException', () => { cause: errorCause, }); - expect(`${error}`).to.be.eql(`HttpException: ${customDescription}`); + expect(`${error}`).toEqual(`HttpException: ${customDescription}`); const { cause } = error; - expect(cause).to.be.eql(errorCause); + expect(cause).toEqual(errorCause); }); it('configures a cause when using a built-in exception with options', () => { @@ -263,8 +263,211 @@ describe('HttpException', () => { const { cause } = error; - expect(cause).to.be.eql(errorCause); + expect(cause).toEqual(errorCause); }); }); + + it('should not set cause when options has no cause', () => { + const error = new HttpException('test', 400, {}); + expect(error.cause).toBeUndefined(); + }); + + it('should not set cause when no options provided', () => { + const error = new HttpException('test', 400); + expect(error.cause).toBeUndefined(); + }); + + it('should preserve default description when using options with cause', () => { + const builtInExceptionsWithDefaults: [Type, string][] = [ + [BadGatewayException, 'Bad Gateway'], + [BadRequestException, 'Bad Request'], + [ConflictException, 'Conflict'], + [ForbiddenException, 'Forbidden'], + [GatewayTimeoutException, 'Gateway Timeout'], + [GoneException, 'Gone'], + [HttpVersionNotSupportedException, 'HTTP Version Not Supported'], + [ImATeapotException, `I'm a teapot`], + [InternalServerErrorException, 'Internal Server Error'], + [MethodNotAllowedException, 'Method Not Allowed'], + [MisdirectedException, 'Misdirected'], + [NotAcceptableException, 'Not Acceptable'], + [NotFoundException, 'Not Found'], + [NotImplementedException, 'Not Implemented'], + [PayloadTooLargeException, 'Payload Too Large'], + [PreconditionFailedException, 'Precondition Failed'], + [RequestTimeoutException, 'Request Timeout'], + [ServiceUnavailableException, 'Service Unavailable'], + [UnauthorizedException, 'Unauthorized'], + [UnprocessableEntityException, 'Unprocessable Entity'], + [UnsupportedMediaTypeException, 'Unsupported Media Type'], + ]; + + builtInExceptionsWithDefaults.forEach( + ([ExceptionClass, expectedDescription]) => { + const error = new ExceptionClass('Custom message', { + cause: errorCause, + }); + + const response = error.getResponse() as { + message: string; + error: string; + }; + + expect(response.error).toEqual(expectedDescription); + }, + ); + }); + }); + + describe('initMessage', () => { + it('should use response.message when response is an object with a message string', () => { + const error = new HttpException({ message: 'custom message' }, 400); + expect(error.message).toBe('custom message'); + }); + + it('should fall back to constructor name when response is an object without message', () => { + const error = new HttpException({ foo: 'bar' }, 400); + expect(error.message).toBe('Http Exception'); + }); + }); + + describe('initName', () => { + it('should set the name to the constructor name', () => { + const error = new HttpException('msg', 400); + expect(error.name).toBe('HttpException'); + }); + + it('should set name based on subclass', () => { + const error = new BadRequestException('msg'); + expect(error.name).toBe('BadRequestException'); + }); + }); + + describe('static helpers', () => { + describe('getDescriptionFrom', () => { + it('should return the string when a string is passed', () => { + expect(HttpException.getDescriptionFrom('desc')).toBe('desc'); + }); + + it('should return the description property when an options object is passed', () => { + expect( + HttpException.getDescriptionFrom({ description: 'from-options' }), + ).toBe('from-options'); + }); + + it('should return undefined when options has no description', () => { + expect(HttpException.getDescriptionFrom({})).toBeUndefined(); + }); + }); + + describe('getHttpExceptionOptionsFrom', () => { + it('should return empty object when a string is passed', () => { + expect(HttpException.getHttpExceptionOptionsFrom('desc')).toEqual({}); + }); + + it('should return the options object as-is', () => { + const options = { cause: new Error('cause'), description: 'desc' }; + expect(HttpException.getHttpExceptionOptionsFrom(options)).toBe( + options, + ); + }); + }); + + describe('extractDescriptionAndOptionsFrom', () => { + it('should extract description string and return empty options', () => { + const result = + HttpException.extractDescriptionAndOptionsFrom('my description'); + expect(result.description).toBe('my description'); + expect(result.httpExceptionOptions).toEqual({}); + }); + + it('should extract description from options object', () => { + const opts = { description: 'from obj', cause: new Error() }; + const result = HttpException.extractDescriptionAndOptionsFrom(opts); + expect(result.description).toBe('from obj'); + expect(result.httpExceptionOptions).toBe(opts); + }); + }); + + describe('createBody with number message', () => { + it('should handle a number as the message', () => { + expect(HttpException.createBody(404, 'Not Found', 404)).toEqual({ + message: 404, + error: 'Not Found', + statusCode: 404, + }); + }); + }); + + describe('createBody with empty string', () => { + it('should treat empty string as nil', () => { + expect(HttpException.createBody('', 'Error', 500)).toEqual({ + message: 'Error', + statusCode: 500, + }); + }); + }); + }); + + describe('when exception is created with a string and a description', () => { + it('should return a response with a message, error and status code', () => { + const exception = new HttpException('Forbidden', HttpStatus.FORBIDDEN); + expect(exception.getResponse()).to.deep.equal('Forbidden'); + }); + + it('should return a response with a message, error, status code and description', () => { + const exception = new HttpException('Forbidden', HttpStatus.FORBIDDEN, { + description: 'some description', + }); + expect(exception.getResponse()).to.deep.equal('Forbidden'); + }); + }); + + describe('when exception is created with a string and a cause', () => { + it('should set a cause', () => { + const error = new Error('An internal error cause'); + const exception = new HttpException( + 'Bad request', + HttpStatus.BAD_REQUEST, + { cause: error }, + ); + expect(exception.cause).to.equal(error); + }); + }); + + describe('when exception is created with an errorCode', () => { + it('should set an errorCode', () => { + const exception = new HttpException( + 'Bad request', + HttpStatus.BAD_REQUEST, + { + errorCode: 'BAD_REQUEST_CODE', + }, + ); + expect(exception.errorCode).to.equal('BAD_REQUEST_CODE'); + }); + + it('should be included in the response body when createBody is called', () => { + const body = HttpException.createBody( + 'Bad Request', + 'Error', + 400, + 'BAD_REQUEST_CODE', + ); + expect(body.errorCode).to.equal('BAD_REQUEST_CODE'); + }); + }); + + describe('when exception is thrown', () => { + it('should return a response with a status code and a message', () => { + const exception = new BadRequestException('error'); + const response = exception.getResponse(); + const message = { + statusCode: 400, + error: 'Bad Request', + message: 'error', + }; + expect(message).to.deep.equal(response); + }); }); }); diff --git a/packages/common/test/file-stream/streamable-file.spec.ts b/packages/common/test/file-stream/streamable-file.spec.ts index b2d8d33f4b8..622ebaa3cd8 100644 --- a/packages/common/test/file-stream/streamable-file.spec.ts +++ b/packages/common/test/file-stream/streamable-file.spec.ts @@ -1,36 +1,34 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; import { Readable } from 'stream'; -import { StreamableFile } from '../../file-stream'; -import { HttpStatus } from '../../enums'; +import { HttpStatus } from '../../enums/index.js'; +import { StreamableFile } from '../../file-stream/index.js'; describe('StreamableFile', () => { describe('when input is a readable stream', () => { it('should assign it to a stream class property', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - expect(streamableFile.getStream()).to.equal(stream); + expect(streamableFile.getStream()).toBe(stream); }); }); describe('when input is an object with "pipe" method', () => { it('should assign it to a stream class property', () => { const stream = { pipe: () => {} }; const streamableFile = new StreamableFile(stream as any); - expect(streamableFile.getStream()).to.equal(stream); + expect(streamableFile.getStream()).toBe(stream); }); }); describe('when input is neither Uint8Array nor has pipe method', () => { it('should not set stream property', () => { const invalidInput = { notPipe: true }; const streamableFile = new StreamableFile(invalidInput as any); - expect(streamableFile.getStream()).to.be.undefined; + expect(streamableFile.getStream()).toBeUndefined(); }); }); describe('when options is empty', () => { it('should return application/octet-stream for type and undefined for others', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - expect(streamableFile.getHeaders()).to.deep.equal({ + expect(streamableFile.getHeaders()).toEqual({ type: 'application/octet-stream', disposition: undefined, length: undefined, @@ -45,7 +43,7 @@ describe('StreamableFile', () => { disposition: 'inline', length: 100, }); - expect(streamableFile.getHeaders()).to.deep.equal({ + expect(streamableFile.getHeaders()).toEqual({ type: 'application/pdf', disposition: 'inline', length: 100, @@ -58,7 +56,7 @@ describe('StreamableFile', () => { const buffer = Buffer.from('test'); const streamableFile = new StreamableFile(buffer); const stream = streamableFile.getStream(); - expect(stream.read()).to.equal(buffer); + expect(stream.read()).toBe(buffer); }); }); describe('when input is a Uint8Array', () => { @@ -66,7 +64,7 @@ describe('StreamableFile', () => { const buffer = Uint8Array.from([0xab, 0xcd, 0xef, 0x00]); const streamableFile = new StreamableFile(buffer); const stream = streamableFile.getStream(); - expect(stream.read()).to.deep.equal(Buffer.from(buffer)); + expect(stream.read()).toEqual(Buffer.from(buffer)); }); }); }); @@ -75,7 +73,7 @@ describe('StreamableFile', () => { it('should return the default error handler', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - expect(streamableFile.errorHandler).to.be.a('function'); + expect(streamableFile.errorHandler).toBeTypeOf('function'); }); describe('default error handler behavior', () => { @@ -86,14 +84,14 @@ describe('StreamableFile', () => { destroyed: true, headersSent: false, statusCode: 200, - end: sinon.spy(), - send: sinon.spy(), + end: vi.fn(), + send: vi.fn(), }; streamableFile.errorHandler(new Error('test error'), res as any); - expect(res.end.called).to.be.false; - expect(res.send.called).to.be.false; + expect(res.end).not.toHaveBeenCalled(); + expect(res.send).not.toHaveBeenCalled(); }); it('should call res.end() when headers are already sent', () => { @@ -103,14 +101,14 @@ describe('StreamableFile', () => { destroyed: false, headersSent: true, statusCode: 200, - end: sinon.spy(), - send: sinon.spy(), + end: vi.fn(), + send: vi.fn(), }; streamableFile.errorHandler(new Error('test error'), res as any); - expect(res.end.calledOnce).to.be.true; - expect(res.send.called).to.be.false; + expect(res.end).toHaveBeenCalledOnce(); + expect(res.send).not.toHaveBeenCalled(); }); it('should set status code to BAD_REQUEST and send error message', () => { @@ -120,15 +118,15 @@ describe('StreamableFile', () => { destroyed: false, headersSent: false, statusCode: 200, - end: sinon.spy(), - send: sinon.spy(), + end: vi.fn(), + send: vi.fn(), }; const error = new Error('test error message'); streamableFile.errorHandler(error, res as any); - expect(res.statusCode).to.equal(HttpStatus.BAD_REQUEST); - expect(res.send.calledOnceWith('test error message')).to.be.true; + expect(res.statusCode).toBe(HttpStatus.BAD_REQUEST); + expect(res.send).toHaveBeenCalledWith('test error message'); }); }); }); @@ -137,21 +135,21 @@ describe('StreamableFile', () => { it('should set a custom error handler', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - const customHandler = sinon.spy(); + const customHandler = vi.fn(); streamableFile.setErrorHandler(customHandler); - expect(streamableFile.errorHandler).to.equal(customHandler); + expect(streamableFile.errorHandler).toBe(customHandler); }); it('should return the instance for chaining', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - const customHandler = sinon.spy(); + const customHandler = vi.fn(); const result = streamableFile.setErrorHandler(customHandler); - expect(result).to.equal(streamableFile); + expect(result).toBe(streamableFile); }); }); @@ -159,14 +157,14 @@ describe('StreamableFile', () => { it('should return the default error logger', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - expect(streamableFile.errorLogger).to.be.a('function'); + expect(streamableFile.errorLogger).toBeTypeOf('function'); }); describe('default error logger behavior', () => { it('should call logger.error with the error', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - const loggerErrorStub = sinon.stub( + const loggerErrorStub = vi.spyOn( (streamableFile as any).logger, 'error', ); @@ -174,8 +172,8 @@ describe('StreamableFile', () => { streamableFile.errorLogger(error); - expect(loggerErrorStub.calledOnceWith(error)).to.be.true; - loggerErrorStub.restore(); + expect(loggerErrorStub).toHaveBeenCalledWith(error); + loggerErrorStub.mockRestore(); }); }); }); @@ -184,21 +182,58 @@ describe('StreamableFile', () => { it('should set a custom error logger', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - const customLogger = sinon.spy(); + const customLogger = vi.fn(); streamableFile.setErrorLogger(customLogger); - expect(streamableFile.errorLogger).to.equal(customLogger); + expect(streamableFile.errorLogger).toBe(customLogger); }); it('should return the instance for chaining', () => { const stream = new Readable(); const streamableFile = new StreamableFile(stream); - const customLogger = sinon.spy(); + const customLogger = vi.fn(); const result = streamableFile.setErrorLogger(customLogger); - expect(result).to.equal(streamableFile); + expect(result).toBe(streamableFile); + }); + }); + + describe('auto-length from Uint8Array', () => { + it('should auto-populate length from Buffer when not provided', () => { + const buffer = Buffer.from('hello world'); + const streamableFile = new StreamableFile(buffer); + + expect(streamableFile.getHeaders().length).toBe(buffer.length); + }); + + it('should auto-populate length from Uint8Array when not provided', () => { + const uint8 = new Uint8Array([1, 2, 3, 4, 5]); + const streamableFile = new StreamableFile(uint8); + + expect(streamableFile.getHeaders().length).toBe(5); + }); + + it('should not override explicitly provided length', () => { + const buffer = Buffer.from('hello'); + const streamableFile = new StreamableFile(buffer, { length: 999 }); + + expect(streamableFile.getHeaders().length).toBe(999); + }); + }); + + describe('getStream', () => { + it('should return a Readable stream from Uint8Array input', () => { + const streamableFile = new StreamableFile(new Uint8Array([1, 2, 3])); + const stream = streamableFile.getStream(); + expect(stream).toBeInstanceOf(Readable); + }); + + it('should return the original Readable when constructed from a stream', () => { + const readable = new Readable({ read() {} }); + const streamableFile = new StreamableFile(readable); + expect(streamableFile.getStream()).toBe(readable); }); }); }); diff --git a/packages/common/test/module-utils/configurable-module.builder.spec.ts b/packages/common/test/module-utils/configurable-module.builder.spec.ts index 26f9c19ed33..69862ab4c77 100644 --- a/packages/common/test/module-utils/configurable-module.builder.spec.ts +++ b/packages/common/test/module-utils/configurable-module.builder.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { Provider } from '../../interfaces'; -import { ConfigurableModuleBuilder } from '../../module-utils'; +import { Provider } from '../../interfaces/index.js'; +import { ConfigurableModuleBuilder } from '../../module-utils/index.js'; describe('ConfigurableModuleBuilder', () => { describe('setExtras', () => { @@ -20,7 +19,7 @@ describe('ConfigurableModuleBuilder', () => { // No type error isGlobal: true, }), - ).to.deep.include({ + ).toMatchObject({ global: true, }); }); @@ -46,8 +45,8 @@ describe('ConfigurableModuleBuilder', () => { folder: 'forRootAsync', }); - expect(capturedExtras).to.deep.equal({ folder: 'forRootAsync' }); - expect(asyncResult).to.have.property( + expect(capturedExtras).toEqual({ folder: 'forRootAsync' }); + expect(asyncResult).toHaveProperty( 'customProperty', 'folder: forRootAsync', ); @@ -59,9 +58,9 @@ describe('ConfigurableModuleBuilder', () => { .setClassMethodName('forRoot') .build(); - expect(ConfigurableModuleClass.forRoot).to.not.be.undefined; - expect(ConfigurableModuleClass.forRootAsync).to.not.be.undefined; - expect((ConfigurableModuleClass as any).register).to.be.undefined; + expect(ConfigurableModuleClass.forRoot).not.toBeUndefined(); + expect(ConfigurableModuleClass.forRootAsync).not.toBeUndefined(); + expect((ConfigurableModuleClass as any).register).toBeUndefined(); }); }); describe('setFactoryMethodName', () => { @@ -77,7 +76,7 @@ describe('ConfigurableModuleBuilder', () => { createOptions() {} }, }), - ).to.not.be.undefined; + ).not.toBeUndefined(); }); }); describe('build', () => { @@ -130,33 +129,31 @@ describe('ConfigurableModuleBuilder', () => { extraProviders: ['test' as any], }); - expect(definition.global).to.equal(true); - expect(definition.providers).to.have.length(5); - expect(definition.providers).to.deep.contain('test'); - expect(definition.providers).to.include.members( - provideInjectionTokensFrom.slice(0, 2), + expect(definition.global).toBe(true); + expect(definition.providers).toHaveLength(5); + expect(definition.providers).toContainEqual('test'); + expect(definition.providers).toEqual( + expect.arrayContaining(provideInjectionTokensFrom.slice(0, 2)), ); - expect(definition.providers).not.to.include( - provideInjectionTokensFrom[2], - ); - expect(MODULE_OPTIONS_TOKEN).to.equal('RANDOM_TEST_MODULE_OPTIONS'); - expect((definition.providers![0] as any).provide).to.equal( + expect(definition.providers).not.toContain(provideInjectionTokensFrom[2]); + expect(MODULE_OPTIONS_TOKEN).toBe('RANDOM_TEST_MODULE_OPTIONS'); + expect((definition.providers![0] as any).provide).toBe( 'RANDOM_TEST_MODULE_OPTIONS', ); try { - expect(ASYNC_OPTIONS_TYPE.imports).to.equal(undefined); + expect(ASYNC_OPTIONS_TYPE.imports).toBe(undefined); } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.equal( + expect(err).toBeInstanceOf(Error); + expect(err.message).toBe( '"ASYNC_OPTIONS_TYPE" is not supposed to be used as a value.', ); } try { - expect(OPTIONS_TYPE.isGlobal).to.equal(undefined); + expect(OPTIONS_TYPE.isGlobal).toBe(undefined); } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.equal( + expect(err).toBeInstanceOf(Error); + expect(err.message).toBe( '"OPTIONS_TYPE" is not supposed to be used as a value.', ); } diff --git a/packages/common/test/module-utils/utils/get-injection-providers.util.spec.ts b/packages/common/test/module-utils/utils/get-injection-providers.util.spec.ts index dfa6364cca3..885f59145d6 100644 --- a/packages/common/test/module-utils/utils/get-injection-providers.util.spec.ts +++ b/packages/common/test/module-utils/utils/get-injection-providers.util.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { Provider } from '../../../interfaces'; -import { getInjectionProviders } from '../../../module-utils/utils/get-injection-providers.util'; +import { Provider } from '../../../interfaces/index.js'; +import { getInjectionProviders } from '../../../module-utils/utils/get-injection-providers.util.js'; describe('getInjectionProviders', () => { it('should take only required providers', () => { @@ -68,6 +67,6 @@ describe('getInjectionProviders', () => { const result = getInjectionProviders(providers, ['e']); - expect(result).to.have.members(expected); + expect(result).toEqual(expect.arrayContaining(expected)); }); }); diff --git a/packages/common/test/pipes/default-value.pipe.spec.ts b/packages/common/test/pipes/default-value.pipe.spec.ts index 6ddad49ac1f..51e47cc1886 100644 --- a/packages/common/test/pipes/default-value.pipe.spec.ts +++ b/packages/common/test/pipes/default-value.pipe.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { DefaultValuePipe } from '../../pipes/default-value.pipe'; +import { DefaultValuePipe } from '../../pipes/default-value.pipe.js'; describe('DefaultValuePipe', () => { const defaultValue = 'default'; @@ -9,12 +8,12 @@ describe('DefaultValuePipe', () => { it('should return original value if one was provided', () => { const value = 'value'; const result = target.transform(value); - expect(result).to.equal(value); + expect(result).toBe(value); }); it('should return default value if no value was provided', () => { const result = target.transform(undefined); - expect(result).to.equal(defaultValue); + expect(result).toBe(defaultValue); }); }); }); diff --git a/packages/common/test/pipes/file/file-type.validator.spec.ts b/packages/common/test/pipes/file/file-type.validator.spec.ts index 28331d3adec..b6808d62eec 100644 --- a/packages/common/test/pipes/file/file-type.validator.spec.ts +++ b/packages/common/test/pipes/file/file-type.validator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { IFile } from '../../../../common/pipes/file/interfaces'; -import { FileTypeValidator } from '../../../pipes'; +import { IFile } from '../../../../common/pipes/file/interfaces/index.js'; +import { FileTypeValidator } from '../../../pipes/index.js'; const pngBuffer = Buffer.from([ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, @@ -28,7 +27,7 @@ describe('FileTypeValidator', () => { buffer: fileData, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); } it('should be able to validate a JPEG file', () => { @@ -49,7 +48,7 @@ describe('FileTypeValidator', () => { mimetype: 'image/jpeg', buffer: jpegBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); }); it('should return true when the file buffer matches the specified regexp', async () => { @@ -65,7 +64,7 @@ describe('FileTypeValidator', () => { buffer: jpegBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); }); it('should return false when the file buffer does not match the specified type', async () => { @@ -81,7 +80,7 @@ describe('FileTypeValidator', () => { buffer: pngBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return false when the file buffer does not match the specified file extension', async () => { @@ -97,7 +96,7 @@ describe('FileTypeValidator', () => { buffer: pngBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return false when no buffer is provided', async () => { @@ -109,7 +108,7 @@ describe('FileTypeValidator', () => { mimetype: 'image/jpeg', } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return false when no file is provided', async () => { @@ -117,7 +116,7 @@ describe('FileTypeValidator', () => { fileType: 'image/jpeg', }); - expect(await fileTypeValidator.isValid()).to.equal(false); + expect(await fileTypeValidator.isValid()).toBe(false); }); it('should return false when no buffer is provided', async () => { @@ -129,7 +128,7 @@ describe('FileTypeValidator', () => { mimetype: 'image/jpeg', } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return true when the file buffer matches the specified regexp', async () => { @@ -145,7 +144,7 @@ describe('FileTypeValidator', () => { buffer: jpegBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); }); it('should return true when no validation options are provided', async () => { @@ -158,7 +157,7 @@ describe('FileTypeValidator', () => { buffer: jpegBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); }); it('should skip magic numbers validation when the skipMagicNumbersValidation is true', async () => { @@ -171,7 +170,7 @@ describe('FileTypeValidator', () => { mimetype: 'image/jpeg', } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); }); it('should return false when the file buffer does not match any known type', async () => { @@ -187,7 +186,7 @@ describe('FileTypeValidator', () => { buffer: unknownBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return false when the buffer is empty', async () => { @@ -201,7 +200,7 @@ describe('FileTypeValidator', () => { buffer: emptyBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return true when fallbackToMimetype is enabled and mimetype matches', async () => { @@ -216,7 +215,7 @@ describe('FileTypeValidator', () => { buffer: shortText, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); }); it('should return false when fallbackToMimetype is enabled but mimetype does not match', async () => { @@ -231,7 +230,7 @@ describe('FileTypeValidator', () => { buffer: shortText, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return true when no buffer is provided but fallbackToMimetype is enabled and mimetype matches', async () => { @@ -244,7 +243,7 @@ describe('FileTypeValidator', () => { mimetype: 'image/jpeg', // matches } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(true); + expect(await fileTypeValidator.isValid(requestFile)).toBe(true); }); it('should return false when no buffer is provided and fallbackToMimetype is enabled but mimetype does not match', async () => { @@ -257,7 +256,7 @@ describe('FileTypeValidator', () => { mimetype: 'image/png', } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); }); @@ -268,7 +267,7 @@ describe('FileTypeValidator', () => { fileType, }); - expect(fileTypeValidator.buildErrorMessage()).to.equal( + expect(fileTypeValidator.buildErrorMessage()).toBe( `Validation failed (expected type is ${fileType})`, ); }); @@ -282,7 +281,7 @@ describe('FileTypeValidator', () => { const file = { mimetype: currentFileType } as IFile; - expect(fileTypeValidator.buildErrorMessage(file)).to.equal( + expect(fileTypeValidator.buildErrorMessage(file)).toBe( `Validation failed (current file type is ${currentFileType}, expected type is ${fileType})`, ); }); @@ -293,7 +292,7 @@ describe('FileTypeValidator', () => { }); const file = { mimetype: 'application/pdf' } as IFile; - expect(fileTypeValidator.buildErrorMessage(file)).to.equal( + expect(fileTypeValidator.buildErrorMessage(file)).toBe( `Validation failed (current file type is application/pdf, expected type is /^image\\//)`, ); }); @@ -304,7 +303,7 @@ describe('FileTypeValidator', () => { }); const file = { mimetype: 'image/png' } as IFile; - expect(fileTypeValidator.buildErrorMessage(file)).to.equal( + expect(fileTypeValidator.buildErrorMessage(file)).toBe( 'Validation failed (current file type is image/png, expected type is jpeg)', ); }); @@ -320,7 +319,7 @@ describe('FileTypeValidator', () => { buffer: textBuffer, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should fail validation for text/csv when magic number detection is enabled', async () => { @@ -335,7 +334,7 @@ describe('FileTypeValidator', () => { buffer: csvFile, } as IFile; - expect(await fileTypeValidator.isValid(requestFile)).to.equal(false); + expect(await fileTypeValidator.isValid(requestFile)).toBe(false); }); it('should return a static custom error message when the file type does not match', async () => { @@ -348,7 +347,7 @@ describe('FileTypeValidator', () => { }); const requestFile = { mimetype: actualFileType } as IFile; - expect(fileTypeValidator.buildErrorMessage(requestFile)).to.equal( + expect(fileTypeValidator.buildErrorMessage(requestFile)).toBe( 'invalid type', ); }); @@ -364,7 +363,7 @@ describe('FileTypeValidator', () => { }); const requestFile = { mimetype: actualFileType } as IFile; - expect(fileTypeValidator.buildErrorMessage(requestFile)).to.equal( + expect(fileTypeValidator.buildErrorMessage(requestFile)).toBe( `Received file type '${actualFileType}', but expected '${expectedFileType}'.`, ); }); diff --git a/packages/common/test/pipes/file/max-file-size.validator.spec.ts b/packages/common/test/pipes/file/max-file-size.validator.spec.ts index 8237bbe4aab..2ed1e733d6a 100644 --- a/packages/common/test/pipes/file/max-file-size.validator.spec.ts +++ b/packages/common/test/pipes/file/max-file-size.validator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { MaxFileSizeValidator } from '../../../pipes'; -import { IFile } from '@nestjs/common/pipes/file/interfaces'; +import { MaxFileSizeValidator } from '../../../pipes/index.js'; +import { IFile } from '@nestjs/common/pipes/file/interfaces/index.js'; describe('MaxFileSizeValidator', () => { const oneKb = 1024; @@ -15,7 +14,7 @@ describe('MaxFileSizeValidator', () => { size: 100, } as any; - expect(maxFileSizeValidator.isValid(requestFile)).to.equal(true); + expect(maxFileSizeValidator.isValid(requestFile)).toBe(true); }); it('should return false when the file size is greater than the maximum size', () => { @@ -27,7 +26,7 @@ describe('MaxFileSizeValidator', () => { size: oneKb + 1, } as any; - expect(maxFileSizeValidator.isValid(requestFile)).to.equal(false); + expect(maxFileSizeValidator.isValid(requestFile)).toBe(false); }); it('should return false when the file size is equal to the maximum size', () => { @@ -39,7 +38,7 @@ describe('MaxFileSizeValidator', () => { size: oneKb, } as any; - expect(maxFileSizeValidator.isValid(requestFile)).to.equal(false); + expect(maxFileSizeValidator.isValid(requestFile)).toBe(false); }); it('should return true when no file provided', () => { @@ -47,7 +46,7 @@ describe('MaxFileSizeValidator', () => { maxSize: oneKb, }); - expect(maxFileSizeValidator.isValid()).to.equal(true); + expect(maxFileSizeValidator.isValid()).toBe(true); }); }); @@ -57,7 +56,7 @@ describe('MaxFileSizeValidator', () => { maxSize: oneKb, }); - expect(maxFileSizeValidator.buildErrorMessage()).to.equal( + expect(maxFileSizeValidator.buildErrorMessage()).toBe( `Validation failed (expected size is less than ${oneKb})`, ); }); @@ -70,7 +69,7 @@ describe('MaxFileSizeValidator', () => { const file = { size: currentFileSize } as any; - expect(maxFileSizeValidator.buildErrorMessage(file)).to.equal( + expect(maxFileSizeValidator.buildErrorMessage(file)).toBe( `Validation failed (current file size is ${currentFileSize}, expected size is less than ${oneKb})`, ); }); @@ -83,7 +82,7 @@ describe('MaxFileSizeValidator', () => { }); const requestFile = { size: currentFileSize } as IFile; - expect(maxFileSizeValidator.buildErrorMessage(requestFile)).to.equal( + expect(maxFileSizeValidator.buildErrorMessage(requestFile)).toBe( 'File size exceeds the limit', ); }); @@ -96,7 +95,7 @@ describe('MaxFileSizeValidator', () => { }); const requestFile = { size: currentFileSize } as IFile; - expect(maxFileSizeValidator.buildErrorMessage(requestFile)).to.equal( + expect(maxFileSizeValidator.buildErrorMessage(requestFile)).toBe( 'File size exceeds the limit', ); }); @@ -110,7 +109,7 @@ describe('MaxFileSizeValidator', () => { }); const requestFile = { size: currentFileSize } as IFile; - expect(maxFileSizeValidator.buildErrorMessage(requestFile)).to.equal( + expect(maxFileSizeValidator.buildErrorMessage(requestFile)).toBe( `Received file size is ${currentFileSize}, but it must be smaller than ${oneKb}.`, ); }); diff --git a/packages/common/test/pipes/file/parse-file-pipe.builder.spec.ts b/packages/common/test/pipes/file/parse-file-pipe.builder.spec.ts index 84c2752a465..3fe3e58ff8a 100644 --- a/packages/common/test/pipes/file/parse-file-pipe.builder.spec.ts +++ b/packages/common/test/pipes/file/parse-file-pipe.builder.spec.ts @@ -1,10 +1,9 @@ -import { expect } from 'chai'; import { + FileTypeValidator, FileValidator, MaxFileSizeValidator, ParseFilePipeBuilder, - FileTypeValidator, -} from '../../../pipes'; +} from '../../../pipes/index.js'; describe('ParseFilePipeBuilder', () => { let parseFilePipeBuilder: ParseFilePipeBuilder; @@ -17,7 +16,7 @@ describe('ParseFilePipeBuilder', () => { describe('when no validator was passed', () => { it('should return a ParseFilePipe with no validators', () => { const parseFilePipe = parseFilePipeBuilder.build(); - expect(parseFilePipe.getValidators()).to.be.empty; + expect(parseFilePipe.getValidators()).toHaveLength(0); }); }); @@ -30,9 +29,9 @@ describe('ParseFilePipeBuilder', () => { .addMaxSizeValidator(options) .build(); - expect(parseFilePipe.getValidators()).to.deep.include( + expect(parseFilePipe.getValidators()).toMatchObject([ new MaxFileSizeValidator(options), - ); + ]); }); }); @@ -45,9 +44,9 @@ describe('ParseFilePipeBuilder', () => { .addFileTypeValidator(options) .build(); - expect(parseFilePipe.getValidators()).to.deep.include( + expect(parseFilePipe.getValidators()).toMatchObject([ new FileTypeValidator(options), - ); + ]); }); }); @@ -71,9 +70,9 @@ describe('ParseFilePipeBuilder', () => { .addValidator(new TestFileValidator(options)) .build(); - expect(parseFilePipe.getValidators()).to.deep.include( + expect(parseFilePipe.getValidators()).toMatchObject([ new TestFileValidator(options), - ); + ]); }); }); @@ -95,7 +94,7 @@ describe('ParseFilePipeBuilder', () => { .addFileTypeValidator(fileTypeValidatorOptions) .build(); - expect(pipeWithFileTypeValidator.getValidators()).not.to.deep.equal( + expect(pipeWithFileTypeValidator.getValidators()).not.toEqual( pipeWithMaxSizeValidator.getValidators(), ); }); diff --git a/packages/common/test/pipes/file/parse-file.pipe.spec.ts b/packages/common/test/pipes/file/parse-file.pipe.spec.ts index 0eecaf98967..3376e25abc3 100644 --- a/packages/common/test/pipes/file/parse-file.pipe.spec.ts +++ b/packages/common/test/pipes/file/parse-file.pipe.spec.ts @@ -1,7 +1,9 @@ -import { HttpStatus } from '../../../enums'; -import { BadRequestException, ConflictException } from '../../../exceptions'; -import { FileValidator, ParseFilePipe } from '../../../pipes'; -import { expect } from 'chai'; +import { HttpStatus } from '../../../enums/index.js'; +import { + BadRequestException, + ConflictException, +} from '../../../exceptions/index.js'; +import { FileValidator, ParseFilePipe } from '../../../pipes/index.js'; class AlwaysValidValidator extends FileValidator { isValid(): boolean { @@ -38,7 +40,7 @@ describe('ParseFilePipe', () => { path: 'some-path', }; - await expect(parseFilePipe.transform(requestFile)).to.eventually.eql( + await expect(parseFilePipe.transform(requestFile)).resolves.toEqual( requestFile, ); }); @@ -54,7 +56,7 @@ describe('ParseFilePipe', () => { path: 'some-path', }; - await expect(parseFilePipe.transform(requestFile)).to.eventually.eql( + await expect(parseFilePipe.transform(requestFile)).resolves.toEqual( requestFile, ); }); @@ -72,7 +74,7 @@ describe('ParseFilePipe', () => { path: 'some-path', }; - await expect(parseFilePipe.transform(requestFile)).to.eventually.eql( + await expect(parseFilePipe.transform(requestFile)).resolves.toEqual( requestFile, ); }); @@ -91,7 +93,7 @@ describe('ParseFilePipe', () => { path: 'some-path', }; - await expect(parseFilePipe.transform(requestFile)).to.be.rejectedWith( + await expect(parseFilePipe.transform(requestFile)).rejects.toThrow( BadRequestException, ); }); @@ -110,7 +112,7 @@ describe('ParseFilePipe', () => { path: 'some-path', }; - await expect(parseFilePipe.transform(requestFile)).to.be.rejectedWith( + await expect(parseFilePipe.transform(requestFile)).rejects.toThrow( ConflictException, ); }); @@ -128,7 +130,7 @@ describe('ParseFilePipe', () => { it('should pass validation if no file is provided', async () => { const requestFile = undefined; - await expect(parseFilePipe.transform(requestFile)).to.eventually.eql( + await expect(parseFilePipe.transform(requestFile)).resolves.toEqual( requestFile, ); }); @@ -145,7 +147,7 @@ describe('ParseFilePipe', () => { it('should throw an error if no file is provided', async () => { const requestFile = undefined; - await expect(parseFilePipe.transform(requestFile)).to.be.rejectedWith( + await expect(parseFilePipe.transform(requestFile)).rejects.toThrow( BadRequestException, ); }); @@ -155,7 +157,7 @@ describe('ParseFilePipe', () => { path: 'some-path', }; - await expect(parseFilePipe.transform(requestFile)).to.eventually.eql( + await expect(parseFilePipe.transform(requestFile)).resolves.toEqual( requestFile, ); }); @@ -171,7 +173,7 @@ describe('ParseFilePipe', () => { it('should throw an error if no file is provided', async () => { const requestFile = undefined; - await expect(parseFilePipe.transform(requestFile)).to.be.rejectedWith( + await expect(parseFilePipe.transform(requestFile)).rejects.toThrow( BadRequestException, ); }); diff --git a/packages/common/test/pipes/parse-array.pipe.spec.ts b/packages/common/test/pipes/parse-array.pipe.spec.ts index 096ab3f9bca..6985ac57b37 100644 --- a/packages/common/test/pipes/parse-array.pipe.spec.ts +++ b/packages/common/test/pipes/parse-array.pipe.spec.ts @@ -1,6 +1,3 @@ -import * as chai from 'chai'; -import { expect } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; import { Type } from 'class-transformer'; import { IsBoolean, @@ -10,10 +7,9 @@ import { IsString, ValidateNested, } from 'class-validator'; -import { BadRequestException } from '../../exceptions'; -import { ArgumentMetadata } from '../../interfaces/features/pipe-transform.interface'; -import { ParseArrayPipe } from '../../pipes/parse-array.pipe'; -chai.use(chaiAsPromised); +import { BadRequestException } from '../../exceptions/index.js'; +import { ArgumentMetadata } from '../../interfaces/features/pipe-transform.interface.js'; +import { ParseArrayPipe } from '../../pipes/parse-array.pipe.js'; describe('ParseArrayPipe', () => { let target: ParseArrayPipe; @@ -26,15 +22,16 @@ describe('ParseArrayPipe', () => { return expect( target.transform(undefined, {} as ArgumentMetadata), - ).to.to.be.rejectedWith(BadRequestException); + ).rejects.toThrow(BadRequestException); }); }); describe('and optional enabled', () => { it('should return undefined', async () => { target = new ParseArrayPipe({ optional: true }); - expect(await target.transform(undefined, {} as ArgumentMetadata)).to - .be.undefined; + expect( + await target.transform(undefined, {} as ArgumentMetadata), + ).toBeUndefined(); }); }); }); @@ -46,17 +43,17 @@ describe('ParseArrayPipe', () => { it('should throw an exception (boolean)', async () => { return expect( target.transform(true, {} as ArgumentMetadata), - ).to.be.rejectedWith(BadRequestException); + ).rejects.toThrow(BadRequestException); }); it('should throw an exception (number)', async () => { return expect( target.transform(3, {} as ArgumentMetadata), - ).to.be.rejectedWith(BadRequestException); + ).rejects.toThrow(BadRequestException); }); it('should throw an exception (object)', async () => { return expect( target.transform({}, {} as ArgumentMetadata), - ).to.be.rejectedWith(BadRequestException); + ).rejects.toThrow(BadRequestException); }); describe('and "optional" is enabled', () => { @@ -68,7 +65,7 @@ describe('ParseArrayPipe', () => { }); return expect( pipe.transform({}, {} as ArgumentMetadata), - ).to.be.rejectedWith(BadRequestException); + ).rejects.toThrow(BadRequestException); }); }); }); @@ -82,19 +79,19 @@ describe('ParseArrayPipe', () => { '1,2.0,3,{},true,null,,', {} as ArgumentMetadata, ), - ).to.be.deep.equal(['1', '2.0', '3', '{}', 'true', 'null', '', '']); + ).toEqual(['1', '2.0', '3', '{}', 'true', 'null', '', '']); target = new ParseArrayPipe({ separator: '/' }); - expect( - await target.transform('1/2/3', {} as ArgumentMetadata), - ).to.be.deep.equal(['1', '2', '3']); + expect(await target.transform('1/2/3', {} as ArgumentMetadata)).toEqual( + ['1', '2', '3'], + ); target = new ParseArrayPipe({ separator: '.' }); - expect( - await target.transform('1.2.3', {} as ArgumentMetadata), - ).to.be.deep.equal(['1', '2', '3']); + expect(await target.transform('1.2.3', {} as ArgumentMetadata)).toEqual( + ['1', '2', '3'], + ); }); describe('and type is specified', () => { @@ -103,7 +100,7 @@ describe('ParseArrayPipe', () => { expect( await target.transform('1.2.3', {} as ArgumentMetadata), - ).to.be.deep.equal([1, 2, 3]); + ).toEqual([1, 2, 3]); target = new ParseArrayPipe({ separator: '.', items: Number }); @@ -111,8 +108,8 @@ describe('ParseArrayPipe', () => { await target.transform('1.2.a.null.3', {} as ArgumentMetadata); throw null; } catch (err) { - expect(err).to.be.instanceOf(BadRequestException); - expect(err.getResponse().message).to.deep.equal( + expect(err).toBeInstanceOf(BadRequestException); + expect(err.getResponse().message).toEqual( '[2] item must be a number', ); } @@ -123,8 +120,8 @@ describe('ParseArrayPipe', () => { await target.transform('1.2.a.null.3', {} as ArgumentMetadata); throw null; } catch (err) { - expect(err).to.be.instanceOf(BadRequestException); - expect(err.getResponse().message).to.deep.equal( + expect(err).toBeInstanceOf(BadRequestException); + expect(err.getResponse().message).toEqual( '[0] item must be a boolean value', ); } @@ -139,8 +136,8 @@ describe('ParseArrayPipe', () => { await target.transform('1.2.a.b.null.3', {} as ArgumentMetadata); throw null; } catch (err) { - expect(err).to.be.instanceOf(BadRequestException); - expect(err.getResponse().message).to.deep.equal([ + expect(err).toBeInstanceOf(BadRequestException); + expect(err.getResponse().message).toEqual([ '[2] item must be a number', '[3] item must be a number', '[4] item must be a number', @@ -164,18 +161,18 @@ describe('ParseArrayPipe', () => { {} as ArgumentMetadata, ); items.forEach(item => { - expect(item).to.be.instanceOf(ArrItem); + expect(item).toBeInstanceOf(ArrItem); }); items = await target.transform('{},{},{}', {} as ArgumentMetadata); items.forEach(item => { - expect(item).to.be.instanceOf(ArrItem); + expect(item).toBeInstanceOf(ArrItem); }); target = new ParseArrayPipe({ items: Number }); expect( await target.transform('1,2.0,3', {} as ArgumentMetadata), - ).to.deep.equal([1, 2, 3]); + ).toEqual([1, 2, 3]); target = new ParseArrayPipe({ items: String }); expect( @@ -183,12 +180,12 @@ describe('ParseArrayPipe', () => { '1,2.0,3,{},true,null,,', {} as ArgumentMetadata, ), - ).to.deep.equal(['1', '2.0', '3', '{}', 'true', 'null', '', '']); + ).toEqual(['1', '2.0', '3', '{}', 'true', 'null', '', '']); target = new ParseArrayPipe({ items: Boolean }); expect( await target.transform('true,false', {} as ArgumentMetadata), - ).to.deep.equal([true, false]); + ).toEqual([true, false]); }); describe('when "stopAtFirstError" is explicitly turned off', () => { it('should validate each item and concat errors', async () => { @@ -210,8 +207,8 @@ describe('ParseArrayPipe', () => { {} as ArgumentMetadata, ); } catch (err) { - expect(err).to.be.instanceOf(BadRequestException); - expect(err.getResponse().message).to.deep.equal([ + expect(err).toBeInstanceOf(BadRequestException); + expect(err.getResponse().message).toEqual([ '[0] number must be a number conforming to the specified constraints', '[1] number must be a number conforming to the specified constraints', ]); @@ -262,8 +259,8 @@ describe('ParseArrayPipe', () => { {} as ArgumentMetadata, ); } catch (err) { - expect(err).to.be.instanceOf(BadRequestException); - expect(err.getResponse().message).to.deep.equal([ + expect(err).toBeInstanceOf(BadRequestException); + expect(err.getResponse().message).toEqual([ '[0] random.title must be a string', '[1] random.isEnabled should not be null or undefined', '[1] random.isEnabled must be a boolean value', @@ -326,8 +323,8 @@ describe('ParseArrayPipe', () => { {} as ArgumentMetadata, ); } catch (err) { - expect(err).to.be.instanceOf(BadRequestException); - expect(err.getResponse().message).to.deep.equal([ + expect(err).toBeInstanceOf(BadRequestException); + expect(err.getResponse().message).toEqual([ '[0] random.0.title must be a string', '[0] random.1.title must be a string', '[1] random.0.isEnabled should not be null or undefined', diff --git a/packages/common/test/pipes/parse-bool.pipe.spec.ts b/packages/common/test/pipes/parse-bool.pipe.spec.ts index 25dc5959bf2..3c1a18821f5 100644 --- a/packages/common/test/pipes/parse-bool.pipe.spec.ts +++ b/packages/common/test/pipes/parse-bool.pipe.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { ArgumentMetadata } from '../../interfaces'; -import { ParseBoolPipe } from '../../pipes/parse-bool.pipe'; +import { ArgumentMetadata } from '../../interfaces/index.js'; +import { ParseBoolPipe } from '../../pipes/parse-bool.pipe.js'; describe('ParseBoolPipe', () => { let target: ParseBoolPipe; @@ -10,13 +9,16 @@ describe('ParseBoolPipe', () => { describe('transform', () => { describe('when validation passes', () => { it('should return boolean', async () => { - expect(await target.transform('true', {} as ArgumentMetadata)).to.be - .true; - expect(await target.transform(true, {} as ArgumentMetadata)).to.be.true; - expect(await target.transform('false', {} as ArgumentMetadata)).to.be - .false; - expect(await target.transform(false, {} as ArgumentMetadata)).to.be - .false; + expect(await target.transform('true', {} as ArgumentMetadata)).toBe( + true, + ); + expect(await target.transform(true, {} as ArgumentMetadata)).toBe(true); + expect(await target.transform('false', {} as ArgumentMetadata)).toBe( + false, + ); + expect(await target.transform(false, {} as ArgumentMetadata)).toBe( + false, + ); }); it('should not throw an error if the value is undefined/null and optional is true', async () => { @@ -25,13 +27,14 @@ describe('ParseBoolPipe', () => { undefined!, {} as ArgumentMetadata, ); - expect(value).to.equal(undefined); + expect(value).toBe(undefined); }); }); describe('when validation fails', () => { it('should throw an error', async () => { - return expect(target.transform('123abc', {} as ArgumentMetadata)).to.be - .rejected; + return expect( + target.transform('123abc', {} as ArgumentMetadata), + ).rejects.toBeDefined(); }); }); }); diff --git a/packages/common/test/pipes/parse-date.pipe.spec.ts b/packages/common/test/pipes/parse-date.pipe.spec.ts index e7f2b78706a..46813fa1a3c 100644 --- a/packages/common/test/pipes/parse-date.pipe.spec.ts +++ b/packages/common/test/pipes/parse-date.pipe.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { BadRequestException } from '../../exceptions'; -import { ParseDatePipe } from '../../pipes/parse-date.pipe'; +import { BadRequestException } from '../../exceptions/index.js'; +import { ParseDatePipe } from '../../pipes/parse-date.pipe.js'; describe('ParseDatePipe', () => { let target: ParseDatePipe; @@ -15,19 +14,19 @@ describe('ParseDatePipe', () => { const date = new Date().toISOString(); const transformedDate = target.transform(date)!; - expect(transformedDate).to.be.instanceOf(Date); - expect(transformedDate.toISOString()).to.equal(date); + expect(transformedDate).toBeInstanceOf(Date); + expect(transformedDate.toISOString()).toBe(date); const asNumber = transformedDate.getTime(); const transformedNumber = target.transform(asNumber)!; - expect(transformedNumber).to.be.instanceOf(Date); - expect(transformedNumber.getTime()).to.equal(asNumber); + expect(transformedNumber).toBeInstanceOf(Date); + expect(transformedNumber.getTime()).toBe(asNumber); }); it('should not throw an error if the value is undefined/null and optional is true', () => { const target = new ParseDatePipe({ optional: true }); const value = target.transform(undefined); - expect(value).to.equal(undefined); + expect(value).toBe(undefined); }); }); describe('when default value is provided', () => { @@ -38,33 +37,17 @@ describe('ParseDatePipe', () => { default: () => defaultValue, }); const value = target.transform(undefined); - expect(value).to.equal(defaultValue); + expect(value).toBe(defaultValue); }); }); describe('when validation fails', () => { it('should throw an error', () => { - try { - target.transform('123abc'); - expect.fail(); - } catch (error) { - expect(error).to.be.instanceOf(BadRequestException); - expect(error.message).to.equal( - 'Validation failed (invalid date format)', - ); - } + expect(() => target.transform('123abc')).toThrow(BadRequestException); }); }); describe('when empty value', () => { it('should throw an error', () => { - try { - target.transform(''); - expect.fail(); - } catch (error) { - expect(error).to.be.instanceOf(BadRequestException); - expect(error.message).to.equal( - 'Validation failed (no Date provided)', - ); - } + expect(() => target.transform('')).toThrow(BadRequestException); }); }); }); diff --git a/packages/common/test/pipes/parse-enum.pipe.spec.ts b/packages/common/test/pipes/parse-enum.pipe.spec.ts index 5182781dcbe..b4c68beef97 100644 --- a/packages/common/test/pipes/parse-enum.pipe.spec.ts +++ b/packages/common/test/pipes/parse-enum.pipe.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { HttpException } from '../../exceptions'; -import { ArgumentMetadata } from '../../interfaces'; -import { ParseEnumPipe } from '../../pipes/parse-enum.pipe'; +import { HttpException } from '../../exceptions/index.js'; +import { ArgumentMetadata } from '../../interfaces/index.js'; +import { ParseEnumPipe } from '../../pipes/parse-enum.pipe.js'; class CustomTestError extends HttpException { constructor() { @@ -23,7 +22,7 @@ describe('ParseEnumPipe', () => { describe('transform', () => { describe('when validation passes', () => { it('should return enum value', async () => { - expect(await target.transform('UP', {} as ArgumentMetadata)).to.equal( + expect(await target.transform('UP', {} as ArgumentMetadata)).toBe( Direction.Up, ); }); @@ -34,14 +33,14 @@ describe('ParseEnumPipe', () => { undefined!, {} as ArgumentMetadata, ); - expect(value).to.equal(undefined); + expect(value).toBe(undefined); }); }); describe('when validation fails', () => { it('should throw an error', async () => { return expect( target.transform('DOWN', {} as ArgumentMetadata), - ).to.be.rejectedWith(CustomTestError); + ).rejects.toThrow(CustomTestError); }); it('should throw an error if enumType is wrong and optional is true', async () => { @@ -51,7 +50,7 @@ describe('ParseEnumPipe', () => { }); return expect( target.transform('DOWN', {} as ArgumentMetadata), - ).to.be.rejectedWith(CustomTestError); + ).rejects.toThrow(CustomTestError); }); }); }); @@ -60,7 +59,7 @@ describe('ParseEnumPipe', () => { try { new ParseEnumPipe(null); } catch (err) { - expect(err.message).to.equal( + expect(err.message).toBe( `"ParseEnumPipe" requires "enumType" argument specified (to validate input values).`, ); } diff --git a/packages/common/test/pipes/parse-float.pipe.spec.ts b/packages/common/test/pipes/parse-float.pipe.spec.ts index 5343c23a616..a5c224a8991 100644 --- a/packages/common/test/pipes/parse-float.pipe.spec.ts +++ b/packages/common/test/pipes/parse-float.pipe.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { HttpException } from '../../exceptions'; -import { ArgumentMetadata } from '../../interfaces'; -import { ParseFloatPipe } from '../../pipes/parse-float.pipe'; +import { HttpException } from '../../exceptions/index.js'; +import { ArgumentMetadata } from '../../interfaces/index.js'; +import { ParseFloatPipe } from '../../pipes/parse-float.pipe.js'; class CustomTestError extends HttpException { constructor() { @@ -20,7 +19,7 @@ describe('ParseFloatPipe', () => { describe('when validation passes', () => { it('should return number', async () => { const num = '3.33'; - expect(await target.transform(num, {} as ArgumentMetadata)).to.equal( + expect(await target.transform(num, {} as ArgumentMetadata)).toBe( parseFloat(num), ); }); @@ -30,14 +29,14 @@ describe('ParseFloatPipe', () => { undefined!, {} as ArgumentMetadata, ); - expect(value).to.equal(undefined); + expect(value).toBe(undefined); }); }); describe('when validation fails', () => { it('should throw an error', async () => { return expect( target.transform('123.123abc', {} as ArgumentMetadata), - ).to.be.rejectedWith(CustomTestError); + ).rejects.toThrow(CustomTestError); }); }); }); diff --git a/packages/common/test/pipes/parse-int.pipe.spec.ts b/packages/common/test/pipes/parse-int.pipe.spec.ts index 5c2370a7422..4964a948b84 100644 --- a/packages/common/test/pipes/parse-int.pipe.spec.ts +++ b/packages/common/test/pipes/parse-int.pipe.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { HttpException } from '../../exceptions'; -import { ArgumentMetadata } from '../../interfaces'; -import { ParseIntPipe } from '../../pipes/parse-int.pipe'; +import { HttpException } from '../../exceptions/index.js'; +import { ArgumentMetadata } from '../../interfaces/index.js'; +import { ParseIntPipe } from '../../pipes/parse-int.pipe.js'; class CustomTestError extends HttpException { constructor() { @@ -20,15 +19,13 @@ describe('ParseIntPipe', () => { describe('when validation passes', () => { it('should return number', async () => { const num = '3'; - expect(await target.transform(num, {} as ArgumentMetadata)).to.equal( + expect(await target.transform(num, {} as ArgumentMetadata)).toBe( parseInt(num, 10), ); }); it('should return negative number', async () => { const num = '-3'; - expect(await target.transform(num, {} as ArgumentMetadata)).to.equal( - -3, - ); + expect(await target.transform(num, {} as ArgumentMetadata)).toBe(-3); }); it('should not throw an error if the value is undefined/null and optional is true', async () => { const target = new ParseIntPipe({ optional: true }); @@ -36,19 +33,19 @@ describe('ParseIntPipe', () => { undefined!, {} as ArgumentMetadata, ); - expect(value).to.equal(undefined); + expect(value).toBe(undefined); }); }); describe('when validation fails', () => { it('should throw an error', async () => { return expect( target.transform('123abc', {} as ArgumentMetadata), - ).to.be.rejectedWith(CustomTestError); + ).rejects.toThrow(CustomTestError); }); it('should throw an error when number has wrong number encoding', async () => { return expect( target.transform('0xFF', {} as ArgumentMetadata), - ).to.be.rejectedWith(CustomTestError); + ).rejects.toThrow(CustomTestError); }); }); }); diff --git a/packages/common/test/pipes/parse-uuid.pipe.spec.ts b/packages/common/test/pipes/parse-uuid.pipe.spec.ts index a28d1c0b74d..82ae5696fba 100644 --- a/packages/common/test/pipes/parse-uuid.pipe.spec.ts +++ b/packages/common/test/pipes/parse-uuid.pipe.spec.ts @@ -1,8 +1,7 @@ -import { expect } from 'chai'; -import { HttpStatus } from '../../enums'; -import { HttpException } from '../../exceptions'; -import { ArgumentMetadata } from '../../interfaces'; -import { ParseUUIDPipe } from '../../pipes/parse-uuid.pipe'; +import { HttpStatus } from '../../enums/index.js'; +import { HttpException } from '../../exceptions/index.js'; +import { ArgumentMetadata } from '../../interfaces/index.js'; +import { ParseUUIDPipe } from '../../pipes/parse-uuid.pipe.js'; class TestException extends HttpException { constructor() { @@ -22,24 +21,24 @@ describe('ParseUUIDPipe', () => { describe('when validation passes', () => { it('should return string if value is uuid v3, v4 or v5', async () => { target = new ParseUUIDPipe({ exceptionFactory }); - expect(await target.transform(v3, {} as ArgumentMetadata)).to.equal(v3); - expect(await target.transform(v4, {} as ArgumentMetadata)).to.equal(v4); - expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5); + expect(await target.transform(v3, {} as ArgumentMetadata)).toBe(v3); + expect(await target.transform(v4, {} as ArgumentMetadata)).toBe(v4); + expect(await target.transform(v5, {} as ArgumentMetadata)).toBe(v5); }); it('should return string if value is uuid v3', async () => { target = new ParseUUIDPipe({ version: '3', exceptionFactory }); - expect(await target.transform(v3, {} as ArgumentMetadata)).to.equal(v3); + expect(await target.transform(v3, {} as ArgumentMetadata)).toBe(v3); }); it('should return string if value is uuid v4', async () => { target = new ParseUUIDPipe({ version: '4', exceptionFactory }); - expect(await target.transform(v4, {} as ArgumentMetadata)).to.equal(v4); + expect(await target.transform(v4, {} as ArgumentMetadata)).toBe(v4); }); it('should return string if value is uuid v5', async () => { target = new ParseUUIDPipe({ version: '5', exceptionFactory }); - expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5); + expect(await target.transform(v5, {} as ArgumentMetadata)).toBe(v5); }); it('should not throw an error if the value is undefined/null and optional is true', async () => { const target = new ParseUUIDPipe({ optional: true }); @@ -47,7 +46,7 @@ describe('ParseUUIDPipe', () => { undefined!, {} as ArgumentMetadata, ); - expect(value).to.equal(undefined); + expect(value).toBe(undefined); }); }); @@ -56,53 +55,53 @@ describe('ParseUUIDPipe', () => { target = new ParseUUIDPipe({ exceptionFactory }); await expect( target.transform('123a', {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); }); it('should throw an error - not a string', async () => { target = new ParseUUIDPipe({ exceptionFactory }); await expect( target.transform(undefined!, {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); }); it('should throw an error - v3', async () => { target = new ParseUUIDPipe({ version: '3', exceptionFactory }); await expect( target.transform('123a', {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); await expect( target.transform(v4, {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); await expect( target.transform(v5, {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); }); it('should throw an error - v4', async () => { target = new ParseUUIDPipe({ version: '4', exceptionFactory }); await expect( target.transform('123a', {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); await expect( target.transform(v3, {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); await expect( target.transform(v5, {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); }); it('should throw an error - v5 ', async () => { target = new ParseUUIDPipe({ version: '5', exceptionFactory }); await expect( target.transform('123a', {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); await expect( target.transform(v3, {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); await expect( target.transform(v4, {} as ArgumentMetadata), - ).to.be.rejectedWith(TestException); + ).rejects.toThrow(TestException); }); }); }); diff --git a/packages/common/test/pipes/standard-schema-validation.pipe.spec.ts b/packages/common/test/pipes/standard-schema-validation.pipe.spec.ts new file mode 100644 index 00000000000..e224dd1ce12 --- /dev/null +++ b/packages/common/test/pipes/standard-schema-validation.pipe.spec.ts @@ -0,0 +1,391 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import { HttpException, HttpStatus } from '../../index.js'; +import { ArgumentMetadata } from '../../interfaces/index.js'; +import { StandardSchemaValidationPipe } from '../../pipes/standard-schema-validation.pipe.js'; + +function createSchema( + validateFn: ( + value: unknown, + ) => StandardSchemaV1.Result | Promise>, +): StandardSchemaV1 { + return { + '~standard': { + version: 1, + vendor: 'test', + validate: validateFn, + }, + }; +} + +describe('StandardSchemaValidationPipe', () => { + let pipe: StandardSchemaValidationPipe; + + beforeEach(() => { + pipe = new StandardSchemaValidationPipe(); + }); + + describe('transform', () => { + describe('when no schema is present in metadata', () => { + it('should return the value unchanged', async () => { + const value = { name: 'test' }; + const result = await pipe.transform(value, { + type: 'body', + } as ArgumentMetadata); + expect(result).toBe(value); + }); + }); + + describe('when schema validation succeeds', () => { + it('should return the validated value', async () => { + const schema = createSchema(value => ({ value })); + const result = await pipe.transform({ name: 'test' }, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(result).toEqual({ name: 'test' }); + }); + + it('should return the transformed value from schema', async () => { + const schema = createSchema(value => ({ + value: { ...(value as any), extra: true }, + })); + const result = await pipe.transform({ name: 'test' }, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(result).toEqual({ name: 'test', extra: true }); + }); + }); + + describe('when schema validation succeeds asynchronously', () => { + it('should return the validated value', async () => { + const schema = createSchema(async value => ({ value })); + const result = await pipe.transform({ name: 'test' }, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(result).toEqual({ name: 'test' }); + }); + }); + + describe('when schema validation fails', () => { + it('should throw a BadRequestException with issue messages', async () => { + const schema = createSchema(() => ({ + issues: [ + { message: 'field is required', path: ['name'] }, + { message: 'must be a string' }, + ], + })); + + await expect( + pipe.transform({}, { type: 'body', schema } as ArgumentMetadata), + ).rejects.toThrow(); + + try { + await pipe.transform({}, { + type: 'body', + schema, + } as ArgumentMetadata); + } catch (error) { + expect(error).toBeInstanceOf(HttpException); + expect((error as HttpException).getStatus()).toBe( + HttpStatus.BAD_REQUEST, + ); + expect((error as HttpException).getResponse()).toEqual({ + statusCode: HttpStatus.BAD_REQUEST, + message: ['field is required', 'must be a string'], + error: 'Bad Request', + }); + } + }); + + it('should throw asynchronously when schema validation fails async', async () => { + const schema = createSchema(async () => ({ + issues: [{ message: 'invalid' }], + })); + + await expect( + pipe.transform({}, { type: 'body', schema } as ArgumentMetadata), + ).rejects.toThrow(); + }); + }); + + describe('when custom errorHttpStatusCode is provided', () => { + it('should throw with the custom status code', async () => { + const customPipe = new StandardSchemaValidationPipe({ + errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, + }); + const schema = createSchema(() => ({ + issues: [{ message: 'invalid' }], + })); + + try { + await customPipe.transform({}, { + type: 'body', + schema, + } as ArgumentMetadata); + } catch (error) { + expect(error).toBeInstanceOf(HttpException); + expect((error as HttpException).getStatus()).toBe( + HttpStatus.UNPROCESSABLE_ENTITY, + ); + } + }); + }); + + describe('when custom exceptionFactory is provided', () => { + it('should use the custom exception factory', async () => { + class CustomError extends Error { + constructor( + public readonly issues: readonly StandardSchemaV1.Issue[], + ) { + super('Custom validation error'); + } + } + + const customPipe = new StandardSchemaValidationPipe({ + exceptionFactory: issues => new CustomError(issues), + }); + const schema = createSchema(() => ({ + issues: [{ message: 'invalid' }], + })); + + try { + await customPipe.transform({}, { + type: 'body', + schema, + } as ArgumentMetadata); + } catch (error) { + expect(error).toBeInstanceOf(CustomError); + expect((error as CustomError).issues).toEqual([ + { message: 'invalid' }, + ]); + } + }); + }); + + describe('when transform is enabled (default)', () => { + it('should return the schema-produced value', async () => { + const input = { name: 'test' }; + const schema = createSchema(value => ({ + value: { ...(value as any), coerced: true }, + })); + const result = await pipe.transform(input, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(result).toEqual({ name: 'test', coerced: true }); + }); + }); + + describe('when transform is explicitly set to true', () => { + it('should return the schema-produced value', async () => { + const transformPipe = new StandardSchemaValidationPipe({ + transform: true, + }); + const input = { name: 'test' }; + const schema = createSchema(value => ({ + value: { ...(value as any), coerced: true }, + })); + const result = await transformPipe.transform(input, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(result).toEqual({ name: 'test', coerced: true }); + }); + }); + + describe('when transform is disabled', () => { + it('should return the original input value', async () => { + const noTransformPipe = new StandardSchemaValidationPipe({ + transform: false, + }); + const input = { name: 'test' }; + const schema = createSchema(value => ({ + value: { ...(value as any), coerced: true }, + })); + const result = await noTransformPipe.transform(input, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(result).toBe(input); + expect(result).toEqual({ name: 'test' }); + }); + }); + + describe('toValidate', () => { + it('should skip validation for custom decorators by default', async () => { + const schema = createSchema(() => ({ + issues: [{ message: 'should not be reached' }], + })); + const result = await pipe.transform({ name: 'test' }, { + type: 'custom', + schema, + } as ArgumentMetadata); + expect(result).toEqual({ name: 'test' }); + }); + + it('should validate custom decorators when validateCustomDecorators is true', async () => { + const customPipe = new StandardSchemaValidationPipe({ + validateCustomDecorators: true, + }); + const schema = createSchema(() => ({ + issues: [{ message: 'invalid' }], + })); + + await expect( + customPipe.transform({}, { + type: 'custom', + schema, + } as ArgumentMetadata), + ).rejects.toThrow(); + }); + + it('should always validate body/query/param types', async () => { + const schema = createSchema(value => ({ value })); + for (const type of ['body', 'query', 'param'] as const) { + const result = await pipe.transform({ ok: true }, { + type, + schema, + } as ArgumentMetadata); + expect(result).toEqual({ ok: true }); + } + }); + }); + + describe('validate', () => { + it('should be overridable', async () => { + class CustomPipe extends StandardSchemaValidationPipe { + protected validate( + value: unknown, + schema: StandardSchemaV1, + options?: Record, + ) { + // Always succeed with a custom value + return { + value: 'custom-result', + } as StandardSchemaV1.Result; + } + } + const customPipe = new CustomPipe(); + const schema = createSchema(() => ({ + issues: [{ message: 'should not be reached' }], + })); + const result = await customPipe.transform('anything', { + type: 'body', + schema, + } as ArgumentMetadata); + expect(result).toBe('custom-result'); + }); + + it('should forward validateOptions to the schema validate function', async () => { + let receivedOptions: unknown; + const schema: StandardSchemaV1 = { + '~standard': { + version: 1, + vendor: 'test', + validate: (value: unknown, options?: unknown) => { + receivedOptions = options; + return { value }; + }, + }, + }; + const optsPipe = new StandardSchemaValidationPipe({ + validateOptions: { strict: true, strip: 'extra' }, + }); + await optsPipe.transform({ name: 'test' }, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(receivedOptions).toEqual({ strict: true, strip: 'extra' }); + }); + + it('should pass undefined when no validateOptions are provided', async () => { + let receivedOptions: unknown = 'NOT_CALLED'; + const schema: StandardSchemaV1 = { + '~standard': { + version: 1, + vendor: 'test', + validate: (value: unknown, options?: unknown) => { + receivedOptions = options; + return { value }; + }, + }, + }; + await pipe.transform({ name: 'test' }, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(receivedOptions).toBeUndefined(); + }); + }); + + describe('stripProtoKeys', () => { + it('should remove __proto__ and prototype from value before validation', async () => { + let receivedValue: any; + const schema = createSchema(value => { + receivedValue = value; + return { value }; + }); + const malicious = JSON.parse( + '{"name": "test", "__proto__": {"polluted": true}, "prototype": {"bad": true}}', + ); + await pipe.transform(malicious, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(receivedValue).not.toHaveProperty('__proto__'); + expect(receivedValue).not.toHaveProperty('prototype'); + expect(receivedValue).toHaveProperty('name', 'test'); + }); + + it('should strip proto keys recursively from nested objects', async () => { + let receivedValue: any; + const schema = createSchema(value => { + receivedValue = value; + return { value }; + }); + const malicious = { + nested: JSON.parse('{"__proto__": {"polluted": true}, "ok": true}'), + }; + await pipe.transform(malicious, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(receivedValue.nested).not.toHaveProperty('__proto__'); + expect(receivedValue.nested).toHaveProperty('ok', true); + }); + + it('should strip proto keys from arrays', async () => { + let receivedValue: any; + const schema = createSchema(value => { + receivedValue = value; + return { value }; + }); + const malicious = [ + JSON.parse('{"__proto__": {"polluted": true}, "ok": true}'), + ]; + await pipe.transform(malicious, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(receivedValue[0]).not.toHaveProperty('__proto__'); + expect(receivedValue[0]).toHaveProperty('ok', true); + }); + + it('should not strip keys from built-in types', async () => { + let receivedValue: any; + const schema = createSchema(value => { + receivedValue = value; + return { value }; + }); + const date = new Date(); + await pipe.transform(date, { + type: 'body', + schema, + } as ArgumentMetadata); + expect(receivedValue).toBe(date); + }); + }); + }); +}); diff --git a/packages/common/test/pipes/validation.pipe.spec.ts b/packages/common/test/pipes/validation.pipe.spec.ts index 3d54bf282dc..4e71e6e0cf5 100644 --- a/packages/common/test/pipes/validation.pipe.spec.ts +++ b/packages/common/test/pipes/validation.pipe.spec.ts @@ -1,20 +1,17 @@ -import * as chai from 'chai'; -import { expect } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; import { Exclude, Expose, Type } from 'class-transformer'; import { IsArray, IsBoolean, IsDefined, + IsNotEmpty, IsOptional, IsString, ValidateNested, } from 'class-validator'; -import { HttpStatus } from '../../enums'; -import { UnprocessableEntityException } from '../../exceptions'; -import { ArgumentMetadata } from '../../interfaces'; -import { ValidationPipe } from '../../pipes/validation.pipe'; -chai.use(chaiAsPromised); +import { HttpStatus } from '../../enums/index.js'; +import { UnprocessableEntityException } from '../../exceptions/index.js'; +import { ArgumentMetadata } from '../../interfaces/index.js'; +import { ValidationPipe } from '../../pipes/validation.pipe.js'; @Exclude() class TestModelInternal { @@ -73,10 +70,10 @@ describe('ValidationPipe', () => { }); it('should return the value unchanged if optional value is not defined', async () => { const testObj = { prop1: 'value1', prop2: 'value2' }; - expect(await target.transform(testObj, {} as any)).to.equal(testObj); + expect(await target.transform(testObj, {} as any)).toBe(testObj); expect( await target.transform(testObj, metadata as any), - ).to.not.be.instanceOf(TestModel); + ).not.toBeInstanceOf(TestModel); }); it('should return the value unchanged if optional value is set undefined', async () => { const testObj = { @@ -84,10 +81,10 @@ describe('ValidationPipe', () => { prop2: 'value2', optionalProp: undefined, }; - expect(await target.transform(testObj, {} as any)).to.equal(testObj); + expect(await target.transform(testObj, {} as any)).toBe(testObj); expect( await target.transform(testObj, metadata as any), - ).to.not.be.instanceOf(TestModel); + ).not.toBeInstanceOf(TestModel); }); it('should return the value unchanged if optional value is null', async () => { const testObj = { @@ -95,10 +92,10 @@ describe('ValidationPipe', () => { prop2: 'value2', optionalProp: null, }; - expect(await target.transform(testObj, {} as any)).to.equal(testObj); + expect(await target.transform(testObj, {} as any)).toBe(testObj); expect( await target.transform(testObj, metadata as any), - ).to.not.be.instanceOf(TestModel); + ).not.toBeInstanceOf(TestModel); }); it('should return the value unchanged if optional value is set', async () => { const testObj = { @@ -106,10 +103,10 @@ describe('ValidationPipe', () => { prop2: 'value2', optionalProp: 'optional value', }; - expect(await target.transform(testObj, {} as any)).to.equal(testObj); + expect(await target.transform(testObj, {} as any)).toBe(testObj); expect( await target.transform(testObj, metadata as any), - ).to.not.be.instanceOf(TestModel); + ).not.toBeInstanceOf(TestModel); }); }); describe('when validation fails', () => { @@ -118,7 +115,9 @@ describe('ValidationPipe', () => { }); it('should throw an error', async () => { const testObj = { prop1: 'value1' }; - return expect(target.transform(testObj, metadata)).to.be.rejected; + return expect( + target.transform(testObj, metadata), + ).rejects.toBeDefined(); }); class TestModel2 { @@ -150,7 +149,7 @@ describe('ValidationPipe', () => { metatype: TestModelWithNested, }); } catch (err) { - expect(err.getResponse().message).to.be.eql([ + expect(err.getResponse().message).toEqual([ 'prop must be a string', 'test.prop1 must be a string', 'test.prop2 must be a boolean value', @@ -176,19 +175,99 @@ describe('ValidationPipe', () => { metatype: TestModelForNestedArrayValidation, }); } catch (err) { - expect(err.getResponse().message).to.be.eql([ + expect(err.getResponse().message).toEqual([ 'prop must be a string', 'test.0.prop1 must be a string', 'test.0.prop2 must be a boolean value', ]); } }); + + describe('when errorFormat is "grouped"', () => { + beforeEach(() => { + target = new ValidationPipe({ errorFormat: 'grouped' }); + }); + + it('should return grouped errors with property paths as keys', async () => { + try { + const model = new TestModelWithNested(); + model.test = new TestModel2(); + await target.transform(model, { + type: 'body', + metatype: TestModelWithNested, + }); + } catch (err) { + expect(err.getResponse().message).to.be.eql({ + prop: ['prop must be a string'], + 'test.prop1': ['prop1 must be a string'], + 'test.prop2': ['prop2 must be a boolean value'], + }); + } + }); + + it('should return grouped errors for nested arrays', async () => { + try { + const model = new TestModelForNestedArrayValidation(); + model.test = [new TestModel2()]; + await target.transform(model, { + type: 'body', + metatype: TestModelForNestedArrayValidation, + }); + } catch (err) { + expect(err.getResponse().message).to.be.eql({ + prop: ['prop must be a string'], + 'test.0.prop1': ['prop1 must be a string'], + 'test.0.prop2': ['prop2 must be a boolean value'], + }); + } + }); + + class NestedChildWithCustomMessage { + @IsNotEmpty({ message: 'Name is required' }) + @IsString({ message: 'Name must be a string' }) + name: string; + } + + class ParentWithCustomMessage { + @IsString() + title: string; + + @IsDefined() + @Type(() => NestedChildWithCustomMessage) + @ValidateNested() + child: NestedChildWithCustomMessage; + } + + it('should preserve custom validation messages without prepending parent path', async () => { + try { + const model = new ParentWithCustomMessage(); + model.child = new NestedChildWithCustomMessage(); + await target.transform(model, { + type: 'body', + metatype: ParentWithCustomMessage, + }); + } catch (err) { + const message = err.getResponse().message; + expect(message.title).to.be.eql(['title must be a string']); + // Custom messages should be preserved without 'child.' prefix in the message itself + expect(message['child.name']).to.have.members([ + 'Name is required', + 'Name must be a string', + ]); + // Verify custom messages don't have parent path prepended + expect(message['child.name']).to.not.include.members([ + 'child.Name is required', + 'child.Name must be a string', + ]); + } + }); + }); }); describe('when validation transforms', () => { it('should return a TestModel instance', async () => { target = new ValidationPipe({ transform: true }); const testObj = { prop1: 'value1', prop2: 'value2', prop3: 'value3' }; - expect(await target.transform(testObj, metadata)).to.be.instanceOf( + expect(await target.transform(testObj, metadata)).toBeInstanceOf( TestModel, ); }); @@ -203,7 +282,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'query', }), - ).to.be.equal(+value); + ).toBe(+value); }); it('should parse undefined to undefined', async () => { target = new ValidationPipe({ transform: true }); @@ -215,7 +294,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'query', }), - ).to.be.undefined; + ).toBeUndefined(); }); }); describe('when input is a path parameter (number)', () => { @@ -229,7 +308,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'param', }), - ).to.be.equal(+value); + ).toBe(+value); }); it('should parse undefined to undefined', async () => { target = new ValidationPipe({ transform: true }); @@ -241,7 +320,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'param', }), - ).to.be.undefined; + ).toBeUndefined(); }); }); describe('when input is a query parameter (boolean)', () => { @@ -255,7 +334,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'query', }), - ).to.be.true; + ).toBe(true); }); it('should parse the string "false" to the boolean false', async () => { target = new ValidationPipe({ transform: true }); @@ -267,7 +346,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'query', }), - ).to.be.false; + ).toBe(false); }); it('should parse an empty string to false', async () => { target = new ValidationPipe({ transform: true }); @@ -279,7 +358,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'query', }), - ).to.be.false; + ).toBe(false); }); it('should parse undefined to undefined', async () => { target = new ValidationPipe({ transform: true }); @@ -291,7 +370,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'query', }), - ).to.be.undefined; + ).toBeUndefined(); }); }); describe('when input is a path parameter (boolean)', () => { @@ -305,7 +384,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'param', }), - ).to.be.true; + ).toBe(true); }); it('should parse the string "false" to boolean false', async () => { target = new ValidationPipe({ transform: true }); @@ -317,7 +396,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'param', }), - ).to.be.false; + ).toBe(false); }); it('should parse an empty string to false', async () => { target = new ValidationPipe({ transform: true }); @@ -329,7 +408,7 @@ describe('ValidationPipe', () => { data: 'test', type: 'param', }), - ).to.be.false; + ).toBe(false); }); it('should parse undefined to undefined', async () => { target = new ValidationPipe({ transform: true }); @@ -341,29 +420,31 @@ describe('ValidationPipe', () => { data: 'test', type: 'param', }), - ).to.be.undefined; + ).toBeUndefined(); }); }); describe('when validation strips', () => { it('should return a TestModel without extra properties', async () => { target = new ValidationPipe({ whitelist: true }); const testObj = { prop1: 'value1', prop2: 'value2', prop3: 'value3' }; - expect( - await target.transform(testObj, metadata), - ).to.not.be.instanceOf(TestModel); - expect( - await target.transform(testObj, metadata), - ).to.not.have.property('prop3'); + expect(await target.transform(testObj, metadata)).not.toBeInstanceOf( + TestModel, + ); + expect(await target.transform(testObj, metadata)).not.toHaveProperty( + 'prop3', + ); }); }); describe('when validation rejects', () => { - it('should throw an error', () => { + it('should throw an error', async () => { target = new ValidationPipe({ forbidNonWhitelisted: true, whitelist: true, }); const testObj = { prop1: 'value1', prop2: 'value2', prop3: 'value3' }; - expect(target.transform(testObj, metadata)).to.eventually.be.rejected; + await expect( + target.transform(testObj, metadata), + ).rejects.toBeDefined(); }); }); describe('when transformation is internal', () => { @@ -379,7 +460,7 @@ describe('ValidationPipe', () => { }; expect( await target.transform(testObj, transformMetadata), - ).to.have.property('propInternal'); + ).toHaveProperty('propInternal'); }); }); describe('when transformation is external', () => { @@ -395,7 +476,7 @@ describe('ValidationPipe', () => { }; expect( await target.transform(testObj, transformMetadata), - ).to.not.have.property('propInternal'); + ).not.toHaveProperty('propInternal'); }); }); }); @@ -406,9 +487,9 @@ describe('ValidationPipe', () => { const testObj = { prop1: 'value1', prop2: 'value2', prop3: 'value3' }; const result = await target.transform(testObj, metadata); - expect(result).to.not.be.instanceOf(TestModel); - expect(result).to.not.have.property('prop3'); - expect(result).to.not.have.property('optionalProp'); + expect(result).not.toBeInstanceOf(TestModel); + expect(result).not.toHaveProperty('prop3'); + expect(result).not.toHaveProperty('optionalProp'); }); it('should return a plain object without extra properties if optional prop is defined', async () => { target = new ValidationPipe({ transform: false, whitelist: true }); @@ -419,9 +500,9 @@ describe('ValidationPipe', () => { optionalProp: 'optional value', }; const result = await target.transform(testObj, metadata); - expect(result).to.not.be.instanceOf(TestModel); - expect(result).to.not.have.property('prop3'); - expect(result).to.have.property('optionalProp'); + expect(result).not.toBeInstanceOf(TestModel); + expect(result).not.toHaveProperty('prop3'); + expect(result).toHaveProperty('optionalProp'); }); it('should return a plain object without extra properties if optional prop is undefined', async () => { target = new ValidationPipe({ transform: false, whitelist: true }); @@ -432,9 +513,9 @@ describe('ValidationPipe', () => { optionalProp: undefined, }; const result = await target.transform(testObj, metadata); - expect(result).to.not.be.instanceOf(TestModel); - expect(result).to.not.have.property('prop3'); - expect(result).to.have.property('optionalProp'); + expect(result).not.toBeInstanceOf(TestModel); + expect(result).not.toHaveProperty('prop3'); + expect(result).toHaveProperty('optionalProp'); }); it('should return a plain object without extra properties if optional prop is null', async () => { target = new ValidationPipe({ transform: false, whitelist: true }); @@ -446,20 +527,22 @@ describe('ValidationPipe', () => { }; const result = await target.transform(testObj, metadata); - expect(result).to.not.be.instanceOf(TestModel); - expect(result).to.not.have.property('prop3'); - expect(result).to.have.property('optionalProp'); + expect(result).not.toBeInstanceOf(TestModel); + expect(result).not.toHaveProperty('prop3'); + expect(result).toHaveProperty('optionalProp'); }); }); describe('when validation rejects', () => { - it('should throw an error', () => { + it('should throw an error', async () => { target = new ValidationPipe({ transform: false, forbidNonWhitelisted: true, whitelist: true, }); const testObj = { prop1: 'value1', prop2: 'value2', prop3: 'value3' }; - expect(target.transform(testObj, metadata)).to.eventually.be.rejected; + await expect( + target.transform(testObj, metadata), + ).rejects.toBeDefined(); }); }); }); @@ -471,11 +554,14 @@ describe('ValidationPipe', () => { { prop1: 'value1', prop2: 'value2', prop3: 'value3' }, ]; - expect(target.transform(testObj, metadata)).to.eventually.be.rejected; - expect(target.transform('string', metadata)).to.eventually.be - .rejected; - expect(target.transform(true, metadata)).to.eventually.be.rejected; - expect(target.transform(3, metadata)).to.eventually.be.rejected; + await expect( + target.transform(testObj, metadata), + ).rejects.toBeDefined(); + await expect( + target.transform('string', metadata), + ).rejects.toBeDefined(); + await expect(target.transform(true, metadata)).rejects.toBeDefined(); + await expect(target.transform(3, metadata)).rejects.toBeDefined(); }); }); describe('otherwise', () => { @@ -488,15 +574,15 @@ describe('ValidationPipe', () => { const objMetadata = { ...metadata, metatype: TestModelNoValidation }; const result = await target.transform(testObj, objMetadata); - expect(result).to.not.be.instanceOf(TestModel); - expect(result).to.be.eql(testObj); + expect(result).not.toBeInstanceOf(TestModel); + expect(result).toEqual(testObj); // primitives - expect(await target.transform('string', objMetadata)).to.be.eql( + expect(await target.transform('string', objMetadata)).toEqual( 'string', ); - expect(await target.transform(3, objMetadata)).to.be.eql(3); - expect(await target.transform(true, objMetadata)).to.be.eql(true); + expect(await target.transform(3, objMetadata)).toEqual(3); + expect(await target.transform(true, objMetadata)).toEqual(true); }); }); }); @@ -516,7 +602,7 @@ describe('ValidationPipe', () => { }; const testObj = { prop1: 'value1', prop2: 'value2' }; - expect(await target.transform(testObj, metadata)).to.not.be.undefined; + expect(await target.transform(testObj, metadata)).not.toBeUndefined(); }); }); describe('when is set to `false`', () => { @@ -547,7 +633,7 @@ describe('ValidationPipe', () => { }; const testObj = { prop1: 'value1', prop2: 'value2' }; - expect(await target.transform(testObj, metadata)).to.not.be.undefined; + expect(await target.transform(testObj, metadata)).not.toBeUndefined(); }); }); }); @@ -564,7 +650,7 @@ describe('ValidationPipe', () => { try { await target.transform(testObj, metadata); } catch (err) { - expect(err).to.be.instanceOf(UnprocessableEntityException); + expect(err).toBeInstanceOf(UnprocessableEntityException); } }); }); @@ -593,7 +679,7 @@ describe('ValidationPipe', () => { target = new ValidationPipe({ expectedType: TestModel }); const testObj = { prop1: 'value1', prop2: 'value2' }; - expect(await target.transform(testObj, m)).to.deep.equal(testObj); + expect(await target.transform(testObj, m)).toEqual(testObj); }); it('should validate against the expected type if presented and metatype is primitive type', async () => { @@ -606,7 +692,7 @@ describe('ValidationPipe', () => { target = new ValidationPipe({ expectedType: TestModel }); const testObj = { prop1: 'value1', prop2: 'value2' }; - expect(await target.transform(testObj, m)).to.deep.equal(testObj); + expect(await target.transform(testObj, m)).toEqual(testObj); }); }); describe('stripProtoKeys', () => { @@ -617,35 +703,35 @@ describe('ValidationPipe', () => { describe('with built-in JavaScript primitives', () => { it('should not throw error when processing Date objects', () => { const value = { date: new Date() }; - expect(() => target['stripProtoKeys'](value)).to.not.throw(); + expect(() => target['stripProtoKeys'](value)).not.toThrow(); }); it('should not throw error when processing RegExp objects', () => { const value = { regex: /test/i }; - expect(() => target['stripProtoKeys'](value)).to.not.throw(); + expect(() => target['stripProtoKeys'](value)).not.toThrow(); }); it('should not throw error when processing Error objects', () => { const value = { error: new Error('test') }; - expect(() => target['stripProtoKeys'](value)).to.not.throw(); + expect(() => target['stripProtoKeys'](value)).not.toThrow(); }); it('should not throw error when processing Map objects', () => { const value = { map: new Map() }; - expect(() => target['stripProtoKeys'](value)).to.not.throw(); + expect(() => target['stripProtoKeys'](value)).not.toThrow(); }); it('should not throw error when processing Set objects', () => { const value = { set: new Set() }; - expect(() => target['stripProtoKeys'](value)).to.not.throw(); + expect(() => target['stripProtoKeys'](value)).not.toThrow(); }); it('should preserve Date object properties', () => { const date = new Date(); const value = { date }; target['stripProtoKeys'](value); - expect(value.date).to.equal(date); - expect(value.date.constructor).to.equal(Date); + expect(value.date).toBe(date); + expect(value.date.constructor).toBe(Date); }); }); @@ -654,19 +740,19 @@ describe('ValidationPipe', () => { const value = { nested: { constructor: 'malicious' } }; target['stripProtoKeys'](value); // Check if 'constructor' is NOT an own property - expect(value.nested).to.not.have.ownProperty('constructor'); + expect(value.nested).not.toHaveProperty('constructor'); }); it('should strip __proto__ from objects', () => { const value = { __proto__: { malicious: 'code' } }; target['stripProtoKeys'](value); - expect(value).to.not.have.ownProperty('__proto__'); + expect(value).not.toHaveProperty('__proto__'); }); it('should strip prototype from objects', () => { const value = { prototype: { malicious: 'code' } }; target['stripProtoKeys'](value); - expect(value).to.not.have.ownProperty('prototype'); + expect(value).not.toHaveProperty('prototype'); }); it('should recursively strip nested objects', () => { @@ -679,8 +765,8 @@ describe('ValidationPipe', () => { }, }; target['stripProtoKeys'](value); - expect(value.level1).to.not.have.ownProperty('constructor'); - expect(value.level1.level2).to.not.have.ownProperty('constructor'); + expect(value.level1).not.toHaveProperty('constructor'); + expect(value.level1.level2).not.toHaveProperty('constructor'); }); }); @@ -693,13 +779,13 @@ describe('ValidationPipe', () => { ], }; target['stripProtoKeys'](value); - expect(value.items[0]).to.not.have.ownProperty('constructor'); - expect(value.items[1]).to.not.have.ownProperty('constructor'); + expect(value.items[0]).not.toHaveProperty('constructor'); + expect(value.items[1]).not.toHaveProperty('constructor'); }); it('should not throw error when array contains Date objects', () => { const value = { dates: [new Date(), new Date()] }; - expect(() => target['stripProtoKeys'](value)).to.not.throw(); + expect(() => target['stripProtoKeys'](value)).not.toThrow(); }); }); @@ -720,7 +806,7 @@ describe('ValidationPipe', () => { }); // This should NOT throw "Cannot delete property 'constructor'" - expect(() => target['stripProtoKeys'](value)).to.not.throw(); + expect(() => target['stripProtoKeys'](value)).not.toThrow(); }); it('should not attempt to delete constructor from built-in types', () => { @@ -733,7 +819,7 @@ describe('ValidationPipe', () => { ]; testCases.forEach(testCase => { - expect(() => target['stripProtoKeys'](testCase)).to.not.throw(); + expect(() => target['stripProtoKeys'](testCase)).not.toThrow(); }); }); }); diff --git a/packages/common/test/serializer/class-serializer.interceptor.spec.ts b/packages/common/test/serializer/class-serializer.interceptor.spec.ts index 5ebc74fbcef..7a0018e080c 100644 --- a/packages/common/test/serializer/class-serializer.interceptor.spec.ts +++ b/packages/common/test/serializer/class-serializer.interceptor.spec.ts @@ -1,29 +1,27 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { of, throwError } from 'rxjs'; -import { ClassSerializerInterceptor } from '../../serializer/class-serializer.interceptor'; -import { ExecutionContext, CallHandler } from '../../interfaces'; -import { StreamableFile } from '../../file-stream'; +import { of } from 'rxjs'; +import { StreamableFile } from '../../file-stream/index.js'; +import { CallHandler, ExecutionContext } from '../../interfaces/index.js'; +import { ClassSerializerInterceptor } from '../../serializer/class-serializer.interceptor.js'; describe('ClassSerializerInterceptor', () => { let interceptor: ClassSerializerInterceptor; let mockReflector: any; let mockTransformerPackage: any; - let sandbox: sinon.SinonSandbox; + let sandbox: any; beforeEach(() => { - sandbox = sinon.createSandbox(); + sandbox = /* sandbox not needed with vitest */ { stub: vi.fn }; mockReflector = { - getAllAndOverride: sandbox.stub(), + getAllAndOverride: vi.fn(), }; mockTransformerPackage = { - classToPlain: sandbox.stub(), - plainToInstance: sandbox.stub(), + classToPlain: vi.fn(), + plainToInstance: vi.fn(), }; }); afterEach(() => { - sandbox.restore(); + vi.restoreAllMocks(); }); describe('constructor', () => { @@ -36,13 +34,13 @@ describe('ClassSerializerInterceptor', () => { interceptor = new ClassSerializerInterceptor(mockReflector, options); - expect(interceptor).to.be.instanceOf(ClassSerializerInterceptor); + expect(interceptor).toBeInstanceOf(ClassSerializerInterceptor); }); it('should use provided transformer package from options', () => { const customTransformer = { - classToPlain: sandbox.stub(), - plainToInstance: sandbox.stub(), + classToPlain: vi.fn(), + plainToInstance: vi.fn(), }; const options = { @@ -51,7 +49,7 @@ describe('ClassSerializerInterceptor', () => { interceptor = new ClassSerializerInterceptor(mockReflector, options); - expect(interceptor).to.be.instanceOf(ClassSerializerInterceptor); + expect(interceptor).toBeInstanceOf(ClassSerializerInterceptor); }); }); @@ -65,32 +63,38 @@ describe('ClassSerializerInterceptor', () => { }); mockExecutionContext = { - getHandler: sandbox.stub(), - getClass: sandbox.stub(), + getHandler: vi.fn(), + getClass: vi.fn(), } as any; mockCallHandler = { - handle: sandbox.stub(), + handle: vi.fn(), } as any; }); - it('should transform plain object response', done => { + it('should transform plain object response', async () => { const response = { id: 1, name: 'Test' }; const transformedResponse = { id: 1, name: 'Test' }; - mockReflector.getAllAndOverride.returns(undefined); - mockTransformerPackage.classToPlain.returns(transformedResponse); - (mockCallHandler.handle as sinon.SinonStub).returns(of(response)); - - interceptor - .intercept(mockExecutionContext, mockCallHandler) - .subscribe(result => { - expect(result).to.equal(transformedResponse); - done(); + mockReflector.getAllAndOverride.mockReturnValue(undefined); + mockTransformerPackage.classToPlain.mockReturnValue(transformedResponse); + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = await interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await new Promise(resolve => { + result$.subscribe(result => { + expect(result).toBe(transformedResponse); + resolve(); }); + }); }); - it('should transform array of objects', done => { + it('should transform array of objects', async () => { const response = [ { id: 1, name: 'Test1' }, { id: 2, name: 'Test2' }, @@ -98,26 +102,29 @@ describe('ClassSerializerInterceptor', () => { const transformedItem1 = { id: 1, name: 'Test1' }; const transformedItem2 = { id: 2, name: 'Test2' }; - mockReflector.getAllAndOverride.returns(undefined); - mockTransformerPackage.classToPlain - .onFirstCall() - .returns(transformedItem1); + mockReflector.getAllAndOverride.mockReturnValue(undefined); mockTransformerPackage.classToPlain - .onSecondCall() - .returns(transformedItem2); - (mockCallHandler.handle as sinon.SinonStub).returns(of(response)); - - interceptor - .intercept(mockExecutionContext, mockCallHandler) - .subscribe(result => { - expect(result).to.be.an('array').with.lengthOf(2); - expect(result[0]).to.equal(transformedItem1); - expect(result[1]).to.equal(transformedItem2); - done(); + .mockReturnValueOnce(transformedItem1) + .mockReturnValueOnce(transformedItem2); + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = await interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await new Promise(resolve => { + result$.subscribe(result => { + expect(result).toHaveLength(2); + expect(result[0]).toBe(transformedItem1); + expect(result[1]).toBe(transformedItem2); + resolve(); }); + }); }); - it('should merge context options with default options', done => { + it('should merge context options with default options', async () => { const response = { id: 1, name: 'Test' }; const defaultOptions = { excludeExtraneousValues: true }; const contextOptions = { groups: ['user'] }; @@ -128,39 +135,55 @@ describe('ClassSerializerInterceptor', () => { ...defaultOptions, }); - mockReflector.getAllAndOverride.returns(contextOptions); - mockTransformerPackage.classToPlain.returns(transformedResponse); - (mockCallHandler.handle as sinon.SinonStub).returns(of(response)); - - interceptor - .intercept(mockExecutionContext, mockCallHandler) - .subscribe(result => { - const callArgs = mockTransformerPackage.classToPlain.getCall(0).args; - expect(callArgs[1]).to.deep.include(defaultOptions); - expect(callArgs[1]).to.deep.include(contextOptions); - done(); + mockReflector.getAllAndOverride.mockReturnValue(contextOptions); + mockTransformerPackage.classToPlain.mockReturnValue(transformedResponse); + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = await interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await new Promise(resolve => { + result$.subscribe(result => { + const callArgs = mockTransformerPackage.classToPlain.mock.calls[0]; + expect(callArgs[1]).toMatchObject(defaultOptions); + expect(callArgs[1]).toMatchObject(contextOptions); + resolve(); }); + }); }); - it('should call reflector with handler and class', done => { + it('should call reflector with handler and class', async () => { const response = { id: 1 }; const mockHandler = {}; const mockClass = {}; - (mockExecutionContext.getHandler as sinon.SinonStub).returns(mockHandler); - (mockExecutionContext.getClass as sinon.SinonStub).returns(mockClass); - mockReflector.getAllAndOverride.returns(undefined); - mockTransformerPackage.classToPlain.returns(response); - (mockCallHandler.handle as sinon.SinonStub).returns(of(response)); - - interceptor - .intercept(mockExecutionContext, mockCallHandler) - .subscribe(() => { - expect(mockReflector.getAllAndOverride.calledOnce).to.be.true; - const args = mockReflector.getAllAndOverride.getCall(0).args; - expect(args[1]).to.deep.equal([mockHandler, mockClass]); - done(); + ( + mockExecutionContext.getHandler as ReturnType + ).mockReturnValue(mockHandler); + ( + mockExecutionContext.getClass as ReturnType + ).mockReturnValue(mockClass); + mockReflector.getAllAndOverride.mockReturnValue(undefined); + mockTransformerPackage.classToPlain.mockReturnValue(response); + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = await interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await new Promise(resolve => { + result$.subscribe(() => { + expect(mockReflector.getAllAndOverride).toHaveBeenCalledOnce(); + const args = mockReflector.getAllAndOverride.mock.calls[0]; + expect(args[1]).toEqual([mockHandler, mockClass]); + resolve(); }); + }); }); }); @@ -172,37 +195,37 @@ describe('ClassSerializerInterceptor', () => { }); it('should return primitive values unchanged', () => { - expect(interceptor.serialize('string' as any, {})).to.equal('string'); - expect(interceptor.serialize(123 as any, {})).to.equal(123); - expect(interceptor.serialize(true as any, {})).to.equal(true); + expect(interceptor.serialize('string' as any, {})).toBe('string'); + expect(interceptor.serialize(123 as any, {})).toBe(123); + expect(interceptor.serialize(true as any, {})).toBe(true); }); it('should return null unchanged', () => { - expect(interceptor.serialize(null as any, {})).to.be.null; + expect(interceptor.serialize(null as any, {})).toBeNull(); }); it('should return undefined unchanged', () => { - expect(interceptor.serialize(undefined as any, {})).to.be.undefined; + expect(interceptor.serialize(undefined as any, {})).toBeUndefined(); }); it('should return StreamableFile unchanged', () => { const streamableFile = new StreamableFile(Buffer.from('test')); const result = interceptor.serialize(streamableFile as any, {}); - expect(result).to.equal(streamableFile); - expect(mockTransformerPackage.classToPlain.called).to.be.false; + expect(result).toBe(streamableFile); + expect(mockTransformerPackage.classToPlain).not.toHaveBeenCalled(); }); it('should transform plain object', () => { const input = { id: 1, name: 'Test' }; const output = { id: 1 }; - mockTransformerPackage.classToPlain.returns(output); + mockTransformerPackage.classToPlain.mockReturnValue(output); const result = interceptor.serialize(input, {}); - expect(result).to.equal(output); - expect(mockTransformerPackage.classToPlain.calledOnce).to.be.true; + expect(result).toBe(output); + expect(mockTransformerPackage.classToPlain).toHaveBeenCalledOnce(); }); it('should transform array of objects', () => { @@ -210,15 +233,16 @@ describe('ClassSerializerInterceptor', () => { const output1 = { id: 1 }; const output2 = { id: 2 }; - mockTransformerPackage.classToPlain.onFirstCall().returns(output1); - mockTransformerPackage.classToPlain.onSecondCall().returns(output2); + mockTransformerPackage.classToPlain + .mockReturnValueOnce(output1) + .mockReturnValueOnce(output2); const result = interceptor.serialize(input, {}); - expect(result).to.be.an('array').with.lengthOf(2); - expect(result[0]).to.equal(output1); - expect(result[1]).to.equal(output2); - expect(mockTransformerPackage.classToPlain.calledTwice).to.be.true; + expect(result).toHaveLength(2); + expect(result[0]).toBe(output1); + expect(result[1]).toBe(output2); + expect(mockTransformerPackage.classToPlain).toHaveBeenCalledTimes(2); }); it('should handle empty array', () => { @@ -226,8 +250,8 @@ describe('ClassSerializerInterceptor', () => { const result = interceptor.serialize(input, {}); - expect(result).to.be.an('array').that.is.empty; - expect(mockTransformerPackage.classToPlain.called).to.be.false; + expect(result).toHaveLength(0); + expect(mockTransformerPackage.classToPlain).not.toHaveBeenCalled(); }); }); @@ -239,25 +263,27 @@ describe('ClassSerializerInterceptor', () => { }); it('should return falsy values unchanged', () => { - expect(interceptor.transformToPlain(null, {})).to.be.null; - expect(interceptor.transformToPlain(undefined, {})).to.be.undefined; - expect(interceptor.transformToPlain(0 as any, {})).to.equal(0); - expect(interceptor.transformToPlain(false as any, {})).to.be.false; - expect(interceptor.transformToPlain('' as any, {})).to.equal(''); + expect(interceptor.transformToPlain(null, {})).toBeNull(); + expect(interceptor.transformToPlain(undefined, {})).toBeUndefined(); + expect(interceptor.transformToPlain(0 as any, {})).toBe(0); + expect(interceptor.transformToPlain(false as any, {})).toBe(false); + expect(interceptor.transformToPlain('' as any, {})).toBe(''); }); it('should use classToPlain when no type option provided', () => { const input = { id: 1, name: 'Test' }; const output = { id: 1 }; - mockTransformerPackage.classToPlain.returns(output); + mockTransformerPackage.classToPlain.mockReturnValue(output); const result = interceptor.transformToPlain(input, {}); - expect(result).to.equal(output); - expect(mockTransformerPackage.classToPlain.calledOnceWith(input, {})).to - .be.true; - expect(mockTransformerPackage.plainToInstance.called).to.be.false; + expect(result).toBe(output); + expect(mockTransformerPackage.classToPlain).toHaveBeenCalledWith( + input, + {}, + ); + expect(mockTransformerPackage.plainToInstance).not.toHaveBeenCalled(); }); it('should use classToPlain when input is instance of options.type', () => { @@ -273,13 +299,13 @@ describe('ClassSerializerInterceptor', () => { const output = { id: 1 }; const options = { type: UserDto }; - mockTransformerPackage.classToPlain.returns(output); + mockTransformerPackage.classToPlain.mockReturnValue(output); const result = interceptor.transformToPlain(input, options); - expect(result).to.equal(output); - expect(mockTransformerPackage.classToPlain.calledOnce).to.be.true; - expect(mockTransformerPackage.plainToInstance.called).to.be.false; + expect(result).toBe(output); + expect(mockTransformerPackage.classToPlain).toHaveBeenCalledOnce(); + expect(mockTransformerPackage.plainToInstance).not.toHaveBeenCalled(); }); it('should convert plain to instance then to plain when type provided but not matching', () => { @@ -294,25 +320,21 @@ describe('ClassSerializerInterceptor', () => { const finalOutput = { id: 1 }; const options = { type: UserDto }; - mockTransformerPackage.plainToInstance.returns(instanceOutput); - mockTransformerPackage.classToPlain.returns(finalOutput); + mockTransformerPackage.plainToInstance.mockReturnValue(instanceOutput); + mockTransformerPackage.classToPlain.mockReturnValue(finalOutput); const result = interceptor.transformToPlain(plainInput, options); - expect(result).to.equal(finalOutput); - expect( - mockTransformerPackage.plainToInstance.calledOnceWith( - UserDto, - plainInput, - options, - ), - ).to.be.true; - expect( - mockTransformerPackage.classToPlain.calledOnceWith( - instanceOutput, - options, - ), - ).to.be.true; + expect(result).toBe(finalOutput); + expect(mockTransformerPackage.plainToInstance).toHaveBeenCalledWith( + UserDto, + plainInput, + options, + ); + expect(mockTransformerPackage.classToPlain).toHaveBeenCalledWith( + instanceOutput, + options, + ); }); it('should handle complex nested objects', () => { @@ -325,11 +347,11 @@ describe('ClassSerializerInterceptor', () => { posts: [{ id: 1 }], }; - mockTransformerPackage.classToPlain.returns(output); + mockTransformerPackage.classToPlain.mockReturnValue(output); const result = interceptor.transformToPlain(input, {}); - expect(result).to.equal(output); + expect(result).toBe(output); }); }); @@ -344,40 +366,40 @@ describe('ClassSerializerInterceptor', () => { const mockHandler = {}; const mockClass = {}; const mockContext = { - getHandler: sandbox.stub().returns(mockHandler), - getClass: sandbox.stub().returns(mockClass), + getHandler: vi.fn().mockReturnValue(mockHandler), + getClass: vi.fn().mockReturnValue(mockClass), } as any; const expectedOptions = { groups: ['admin'] }; - mockReflector.getAllAndOverride.returns(expectedOptions); + mockReflector.getAllAndOverride.mockReturnValue(expectedOptions); const result = (interceptor as any).getContextOptions(mockContext); - expect(mockReflector.getAllAndOverride.calledOnce).to.be.true; - const callArgs = mockReflector.getAllAndOverride.getCall(0).args; - expect(callArgs[1]).to.deep.equal([mockHandler, mockClass]); - expect(result).to.equal(expectedOptions); + expect(mockReflector.getAllAndOverride).toHaveBeenCalledOnce(); + const callArgs = mockReflector.getAllAndOverride.mock.calls[0]; + expect(callArgs[1]).toEqual([mockHandler, mockClass]); + expect(result).toBe(expectedOptions); }); it('should return undefined when no metadata exists', () => { const mockContext = { - getHandler: sandbox.stub().returns({}), - getClass: sandbox.stub().returns({}), + getHandler: vi.fn().mockReturnValue({}), + getClass: vi.fn().mockReturnValue({}), } as any; - mockReflector.getAllAndOverride.returns(undefined); + mockReflector.getAllAndOverride.mockReturnValue(undefined); const result = (interceptor as any).getContextOptions(mockContext); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); it('should respect handler metadata over class metadata', () => { const mockHandler = {}; const mockClass = {}; const mockContext = { - getHandler: sandbox.stub().returns(mockHandler), - getClass: sandbox.stub().returns(mockClass), + getHandler: vi.fn().mockReturnValue(mockHandler), + getClass: vi.fn().mockReturnValue(mockClass), } as any; // getAllAndOverride should merge with handler taking precedence @@ -385,15 +407,15 @@ describe('ClassSerializerInterceptor', () => { groups: ['user'], excludeExtraneousValues: true, }; - mockReflector.getAllAndOverride.returns(handlerOptions); + mockReflector.getAllAndOverride.mockReturnValue(handlerOptions); const result = (interceptor as any).getContextOptions(mockContext); - expect(result).to.deep.equal(handlerOptions); + expect(result).toEqual(handlerOptions); // Verify it's checking handler first, then class - const callArgs = mockReflector.getAllAndOverride.getCall(0).args; - expect(callArgs[1][0]).to.equal(mockHandler); - expect(callArgs[1][1]).to.equal(mockClass); + const callArgs = mockReflector.getAllAndOverride.mock.calls[0]; + expect(callArgs[1][0]).toBe(mockHandler); + expect(callArgs[1][1]).toBe(mockClass); }); }); @@ -413,19 +435,16 @@ describe('ClassSerializerInterceptor', () => { ]; mockTransformerPackage.classToPlain - .onCall(0) - .returns({ id: 1, name: 'Test' }); - mockTransformerPackage.classToPlain.onCall(1).returns(null); - mockTransformerPackage.classToPlain.onCall(2).returns(undefined); - mockTransformerPackage.classToPlain - .onCall(3) - .returns({ id: 2, name: 'Test2' }); + .mockReturnValueOnce({ id: 1, name: 'Test' }) + .mockReturnValueOnce(null) + .mockReturnValueOnce(undefined) + .mockReturnValueOnce({ id: 2, name: 'Test2' }); const result = interceptor.serialize(input, {}); - expect(result).to.be.an('array').with.lengthOf(4); - expect(result[1]).to.be.null; - expect(result[2]).to.be.undefined; + expect(result).toHaveLength(4); + expect(result[1]).toBeNull(); + expect(result[2]).toBeUndefined(); }); it('should not transform when response is not an object', () => { @@ -433,19 +452,19 @@ describe('ClassSerializerInterceptor', () => { const result = interceptor.serialize(input as any, {}); - expect(result).to.equal(input); - expect(mockTransformerPackage.classToPlain.called).to.be.false; + expect(result).toBe(input); + expect(mockTransformerPackage.classToPlain).not.toHaveBeenCalled(); }); it('should handle Date objects', () => { const date = new Date(); const output = { date: date.toISOString() }; - mockTransformerPackage.classToPlain.returns(output); + mockTransformerPackage.classToPlain.mockReturnValue(output); const result = interceptor.serialize({ date } as any, {}); - expect(mockTransformerPackage.classToPlain.calledOnce).to.be.true; + expect(mockTransformerPackage.classToPlain).toHaveBeenCalledOnce(); }); it('should pass through options to transformer', () => { @@ -456,13 +475,16 @@ describe('ClassSerializerInterceptor', () => { strategy: 'excludeAll', }; - mockTransformerPackage.classToPlain.returns({ id: 1, name: 'Test' }); + mockTransformerPackage.classToPlain.mockReturnValue({ + id: 1, + name: 'Test', + }); interceptor.transformToPlain(input, options as any); - expect(mockTransformerPackage.classToPlain.calledOnce).to.be.true; - const callArgs = mockTransformerPackage.classToPlain.getCall(0).args; - expect(callArgs[1]).to.deep.include(options); + expect(mockTransformerPackage.classToPlain).toHaveBeenCalledOnce(); + const callArgs = mockTransformerPackage.classToPlain.mock.calls[0]; + expect(callArgs[1]).toMatchObject(options); }); }); }); diff --git a/packages/common/test/serializer/standard-schema-serializer.interceptor.spec.ts b/packages/common/test/serializer/standard-schema-serializer.interceptor.spec.ts new file mode 100644 index 00000000000..0f5c10dc17c --- /dev/null +++ b/packages/common/test/serializer/standard-schema-serializer.interceptor.spec.ts @@ -0,0 +1,348 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import { lastValueFrom, of } from 'rxjs'; +import { StreamableFile } from '../../file-stream/index.js'; +import { CallHandler, ExecutionContext } from '../../interfaces/index.js'; +import { StandardSchemaSerializerInterceptor } from '../../serializer/standard-schema-serializer.interceptor.js'; + +function createSchema( + validateFn: ( + value: unknown, + options?: StandardSchemaV1.Options, + ) => StandardSchemaV1.Result | Promise>, +): StandardSchemaV1 { + return { + '~standard': { + version: 1, + vendor: 'test', + validate: validateFn, + }, + }; +} + +describe('StandardSchemaSerializerInterceptor', () => { + let interceptor: StandardSchemaSerializerInterceptor; + let mockReflector: any; + let mockExecutionContext: ExecutionContext; + let mockCallHandler: CallHandler; + + beforeEach(() => { + mockReflector = { + getAllAndOverride: vi.fn().mockReturnValue(undefined), + }; + + mockExecutionContext = { + getHandler: vi.fn(), + getClass: vi.fn(), + } as any; + + mockCallHandler = { + handle: vi.fn(), + } as any; + }); + + describe('intercept', () => { + describe('when no schema is provided', () => { + it('should return the response unchanged', async () => { + interceptor = new StandardSchemaSerializerInterceptor(mockReflector); + const response = { id: 1, name: 'Test' }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + const result = await lastValueFrom(result$); + expect(result).toBe(response); + }); + }); + + describe('when schema is provided via default options', () => { + it('should serialize the response through the schema', async () => { + const schema = createSchema(value => ({ + value: { ...(value as any), serialized: true }, + })); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + const response = { id: 1, name: 'Test' }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + const result = await lastValueFrom(result$); + expect(result).toEqual({ id: 1, name: 'Test', serialized: true }); + }); + }); + + describe('when schema is provided via @SerializeOptions()', () => { + it('should use the context schema over the default', async () => { + const defaultSchema = createSchema(value => ({ + value: { ...(value as any), fromDefault: true }, + })); + const contextSchema = createSchema(value => ({ + value: { ...(value as any), fromContext: true }, + })); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema: defaultSchema, + }); + mockReflector.getAllAndOverride.mockReturnValue({ + schema: contextSchema, + }); + const response = { id: 1 }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + const result = await lastValueFrom(result$); + expect(result).toEqual({ id: 1, fromContext: true }); + }); + }); + + describe('when response is an array', () => { + it('should serialize each item through the schema', async () => { + const schema = createSchema(value => ({ + value: { ...(value as any), serialized: true }, + })); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + const response = [{ id: 1 }, { id: 2 }]; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + const result = await lastValueFrom(result$); + expect(result).toEqual([ + { id: 1, serialized: true }, + { id: 2, serialized: true }, + ]); + }); + }); + + describe('when response is not an object', () => { + it('should return primitive values unchanged', async () => { + const schema = createSchema(() => ({ + issues: [{ message: 'should not be called' }], + })); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + (mockCallHandler.handle as ReturnType).mockReturnValue( + of('plain string'), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + const result = await lastValueFrom(result$); + expect(result).toBe('plain string'); + }); + }); + + describe('when response is a StreamableFile', () => { + it('should return the streamable file unchanged', async () => { + const schema = createSchema(() => ({ + issues: [{ message: 'should not be called' }], + })); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + const file = new StreamableFile(Buffer.from('test')); + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(file), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + const result = await lastValueFrom(result$); + expect(result).toBe(file); + }); + }); + + describe('when schema validation fails', () => { + it('should throw an error with issue messages', async () => { + const schema = createSchema(() => ({ + issues: [ + { message: 'field is required' }, + { message: 'must be a number' }, + ], + })); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + const response = { id: 1 }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await expect(lastValueFrom(result$)).rejects.toThrow( + 'Serialization failed: field is required, must be a number', + ); + }); + }); + + describe('when async schema is used', () => { + it('should resolve the async validation result', async () => { + const schema = createSchema(async value => ({ + value: { ...(value as any), async: true }, + })); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + const response = { id: 1 }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + const result = await lastValueFrom(result$); + expect(result).toEqual({ id: 1, async: true }); + }); + }); + }); + + describe('transformToPlain', () => { + it('should return falsy values as-is', async () => { + interceptor = new StandardSchemaSerializerInterceptor(mockReflector); + const schema = createSchema(() => ({ + issues: [{ message: 'should not be called' }], + })); + expect(await interceptor.transformToPlain(null, schema)).toBeNull(); + expect( + await interceptor.transformToPlain(undefined, schema), + ).toBeUndefined(); + expect(await interceptor.transformToPlain(0, schema)).toBe(0); + expect(await interceptor.transformToPlain('', schema)).toBe(''); + }); + }); + + describe('validateOptions', () => { + it('should forward default validateOptions to the schema', async () => { + const validateSpy = vi.fn((_value, _options) => ({ + value: { ok: true }, + })); + const schema = createSchema(validateSpy); + const opts: StandardSchemaV1.Options = { + libraryOptions: { strip: true }, + }; + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + validateOptions: opts, + }); + const response = { id: 1 }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await lastValueFrom(result$); + expect(validateSpy).toHaveBeenCalledWith(response, opts); + }); + + it('should forward context validateOptions from @SerializeOptions()', async () => { + const validateSpy = vi.fn((_value, _options) => ({ + value: { ok: true }, + })); + const schema = createSchema(validateSpy); + const contextOpts: StandardSchemaV1.Options = { + libraryOptions: { mode: 'strict' }, + }; + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + mockReflector.getAllAndOverride.mockReturnValue({ + validateOptions: contextOpts, + }); + const response = { id: 1 }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await lastValueFrom(result$); + expect(validateSpy).toHaveBeenCalledWith(response, contextOpts); + }); + + it('should let context validateOptions override default', async () => { + const validateSpy = vi.fn((_value, _options) => ({ + value: { ok: true }, + })); + const schema = createSchema(validateSpy); + const defaultOpts: StandardSchemaV1.Options = { + libraryOptions: { from: 'default' }, + }; + const contextOpts: StandardSchemaV1.Options = { + libraryOptions: { from: 'context' }, + }; + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + validateOptions: defaultOpts, + }); + mockReflector.getAllAndOverride.mockReturnValue({ + validateOptions: contextOpts, + }); + const response = { id: 1 }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await lastValueFrom(result$); + expect(validateSpy).toHaveBeenCalledWith(response, contextOpts); + }); + + it('should pass undefined when no validateOptions are set', async () => { + const validateSpy = vi.fn((_value, _options) => ({ + value: { ok: true }, + })); + const schema = createSchema(validateSpy); + interceptor = new StandardSchemaSerializerInterceptor(mockReflector, { + schema, + }); + const response = { id: 1 }; + (mockCallHandler.handle as ReturnType).mockReturnValue( + of(response), + ); + + const result$ = interceptor.intercept( + mockExecutionContext, + mockCallHandler, + ); + await lastValueFrom(result$); + expect(validateSpy).toHaveBeenCalledWith(response, undefined); + }); + }); +}); diff --git a/packages/common/test/services/logger.service.spec.ts b/packages/common/test/services/logger.service.spec.ts index 718957f4285..5db5285171b 100644 --- a/packages/common/test/services/logger.service.spec.ts +++ b/packages/common/test/services/logger.service.spec.ts @@ -1,21 +1,24 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ConsoleLogger, Logger, LoggerService, LogLevel } from '../../services'; +import { + ConsoleLogger, + Logger, + LoggerService, + LogLevel, +} from '../../services/index.js'; describe('Logger', () => { describe('[static methods]', () => { describe('when the default logger is used', () => { - let processStdoutWriteSpy: sinon.SinonSpy; - let processStderrWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; + let processStderrWriteSpy: ReturnType; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); - processStderrWriteSpy = sinon.spy(process.stderr, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); + processStderrWriteSpy = vi.spyOn(process.stderr, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); - processStderrWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); + processStderrWriteSpy.mockRestore(); }); it('should print one message to the console', () => { @@ -24,11 +27,11 @@ describe('Logger', () => { Logger.log(message, context); - expect(processStdoutWriteSpy.calledOnce).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print one message without context to the console', () => { @@ -36,8 +39,8 @@ describe('Logger', () => { Logger.log(message); - expect(processStdoutWriteSpy.calledOnce).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message); + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print multiple messages to the console', () => { @@ -46,27 +49,21 @@ describe('Logger', () => { Logger.log(messages[0], messages[1], messages[2], context); - expect(processStdoutWriteSpy.calledThrice).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( + expect(processStdoutWriteSpy).toHaveBeenCalledTimes(3); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( - messages[0], - ); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(messages[0]); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include( + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include( - messages[1], - ); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain(messages[1]); - expect(processStdoutWriteSpy.thirdCall.firstArg).to.include( + expect(processStdoutWriteSpy.mock.calls[2][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.thirdCall.firstArg).to.include( - messages[2], - ); + expect(processStdoutWriteSpy.mock.calls[2][0]).toContain(messages[2]); }); it('should print one error to the console with context', () => { @@ -75,11 +72,11 @@ describe('Logger', () => { Logger.error(message, context); - expect(processStderrWriteSpy.calledOnce).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy).toHaveBeenCalledOnce(); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print one error to the console with stacktrace', () => { @@ -88,12 +85,10 @@ describe('Logger', () => { Logger.error(message, stacktrace); - expect(processStderrWriteSpy.calledTwice).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.not.include(`[]`); - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); - expect(processStderrWriteSpy.secondCall.firstArg).to.equal( - stacktrace + '\n', - ); + expect(processStderrWriteSpy).toHaveBeenCalledTimes(2); + expect(processStderrWriteSpy.mock.calls[0][0]).not.toContain(`[]`); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); + expect(processStderrWriteSpy.mock.calls[1][0]).toBe(stacktrace + '\n'); }); it('should print one error without context to the console', () => { @@ -101,8 +96,8 @@ describe('Logger', () => { Logger.error(message); - expect(processStderrWriteSpy.calledOnce).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); + expect(processStderrWriteSpy).toHaveBeenCalledOnce(); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print error object without context to the console', () => { @@ -110,8 +105,8 @@ describe('Logger', () => { Logger.error(error); - expect(processStderrWriteSpy.calledOnce).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy).toHaveBeenCalledOnce(); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `Error: Random text here`, ); }); @@ -123,12 +118,12 @@ describe('Logger', () => { Logger.error(error); - expect(processStderrWriteSpy.calledOnce).to.be.true; + expect(processStderrWriteSpy).toHaveBeenCalledOnce(); - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `Object(${Object.keys(error).length})`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `randomError: \x1b[33mtrue`, ); }); @@ -140,19 +135,15 @@ describe('Logger', () => { Logger.error(message, stacktrace, context); - expect(processStderrWriteSpy.calledTwice).to.be.true; + expect(processStderrWriteSpy).toHaveBeenCalledTimes(2); - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); - expect(processStderrWriteSpy.secondCall.firstArg).to.equal( - stacktrace + '\n', - ); - expect(processStderrWriteSpy.secondCall.firstArg).to.not.include( - context, - ); + expect(processStderrWriteSpy.mock.calls[1][0]).toBe(stacktrace + '\n'); + expect(processStderrWriteSpy.mock.calls[1][0]).not.toContain(context); }); it('should print multiple 2 errors and one stacktrace to the console', () => { @@ -162,42 +153,38 @@ describe('Logger', () => { Logger.error(messages[0], messages[1], stack, context); - expect(processStderrWriteSpy.calledThrice).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy).toHaveBeenCalledTimes(3); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include( - messages[0], - ); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(messages[0]); - expect(processStderrWriteSpy.secondCall.firstArg).to.include( + expect(processStderrWriteSpy.mock.calls[1][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.secondCall.firstArg).to.include( - messages[1], - ); + expect(processStderrWriteSpy.mock.calls[1][0]).toContain(messages[1]); - expect(processStderrWriteSpy.thirdCall.firstArg).to.not.include( + expect(processStderrWriteSpy.mock.calls[2][0]).not.toContain( `[${context}]`, ); - expect(processStderrWriteSpy.thirdCall.firstArg).to.equal(stack + '\n'); + expect(processStderrWriteSpy.mock.calls[2][0]).toBe(stack + '\n'); }); }); describe('when the default logger is used and json mode is enabled', () => { const logger = new ConsoleLogger({ json: true }); - let processStdoutWriteSpy: sinon.SinonSpy; - let processStderrWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; + let processStderrWriteSpy: ReturnType; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); - processStderrWriteSpy = sinon.spy(process.stderr, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); + processStderrWriteSpy = vi.spyOn(process.stderr, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); - processStderrWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); + processStderrWriteSpy.mockRestore(); }); it('should print error with stack as JSON to the console', () => { @@ -206,33 +193,33 @@ describe('Logger', () => { logger.error(error.message, error.stack); - const json = JSON.parse(processStderrWriteSpy.firstCall?.firstArg); + const json = JSON.parse(processStderrWriteSpy.mock.calls[0]?.[0]); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('error'); - expect(json.message).to.equal(errorMessage); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('error'); + expect(json.message).toBe(errorMessage); }); it('should log out to stdout as JSON', () => { const message = 'message 1'; logger.log(message); - const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg); + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0]?.[0]); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('log'); - expect(json.message).to.equal(message); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('log'); + expect(json.message).toBe(message); }); it('should log out an error to stderr as JSON', () => { const message = 'message 1'; logger.error(message); - const json = JSON.parse(processStderrWriteSpy.firstCall?.firstArg); + const json = JSON.parse(processStderrWriteSpy.mock.calls[0]?.[0]); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('error'); - expect(json.message).to.equal(message); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('error'); + expect(json.message).toBe(message); }); it('should log Map object', () => { const map = new Map([ @@ -242,11 +229,11 @@ describe('Logger', () => { logger.log(map); - const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg); + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0]?.[0]); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('log'); - expect(json.message).to.equal( + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('log'); + expect(json.message).toBe( `Map(2) { 'key1' => 'value1', 'key2' => 'value2' }`, ); }); @@ -255,50 +242,50 @@ describe('Logger', () => { logger.log(set); - const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg); + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0]?.[0]); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('log'); - expect(json.message).to.equal(`Set(2) { 'value1', 'value2' }`); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('log'); + expect(json.message).toBe(`Set(2) { 'value1', 'value2' }`); }); it('should log bigint', () => { const bigInt = BigInt(9007199254740991); logger.log(bigInt); - const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg); + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0]?.[0]); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('log'); - expect(json.message).to.equal('9007199254740991'); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('log'); + expect(json.message).toBe('9007199254740991'); }); it('should log symbol', () => { const symbol = Symbol('test'); logger.log(symbol); - const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg); + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0]?.[0]); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('log'); - expect(json.message).to.equal('Symbol(test)'); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('log'); + expect(json.message).toBe('Symbol(test)'); }); }); describe('when the default logger is used, json mode is enabled and compact is false (utils.inspect)', () => { const logger = new ConsoleLogger({ json: true, compact: false }); - let processStdoutWriteSpy: sinon.SinonSpy; - let processStderrWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; + let processStderrWriteSpy: ReturnType; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); - processStderrWriteSpy = sinon.spy(process.stderr, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); + processStderrWriteSpy = vi.spyOn(process.stderr, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); - processStderrWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); + processStderrWriteSpy.mockRestore(); }); it('should log out to stdout as JSON (utils.inspect)', () => { @@ -307,12 +294,12 @@ describe('Logger', () => { logger.log(message); const json = convertInspectToJSON( - processStdoutWriteSpy.firstCall?.firstArg, + processStdoutWriteSpy.mock.calls[0]?.[0], ); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('log'); - expect(json.message).to.equal(message); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('log'); + expect(json.message).toBe(message); }); it('should log out an error to stderr as JSON (utils.inspect)', () => { @@ -321,21 +308,21 @@ describe('Logger', () => { logger.error(message); const json = convertInspectToJSON( - processStderrWriteSpy.firstCall?.firstArg, + processStderrWriteSpy.mock.calls[0]?.[0], ); - expect(json.pid).to.equal(process.pid); - expect(json.level).to.equal('error'); - expect(json.message).to.equal(message); + expect(json.pid).toBe(process.pid); + expect(json.level).toBe('error'); + expect(json.message).toBe(message); }); }); describe('when logging is disabled', () => { - let processStdoutWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; let previousLoggerRef: LoggerService; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); previousLoggerRef = Logger['localInstanceRef'] || Logger['staticInstanceRef']; @@ -343,7 +330,7 @@ describe('Logger', () => { }); afterEach(() => { - processStdoutWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); Logger.overrideLogger(previousLoggerRef); }); @@ -354,7 +341,7 @@ describe('Logger', () => { Logger.log(message, context); - expect(processStdoutWriteSpy.called).to.be.false; + expect(processStdoutWriteSpy).not.toHaveBeenCalled(); }); }); describe('when custom logger is being used', () => { @@ -381,24 +368,24 @@ describe('Logger', () => { const message = 'random message'; const context = 'RandomContext'; - const customLoggerLogSpy = sinon.spy(customLogger, 'log'); + const customLoggerLogSpy = vi.spyOn(customLogger, 'log'); Logger.log(message, context); - expect(customLoggerLogSpy.called).to.be.true; - expect(customLoggerLogSpy.calledWith(message, context)).to.be.true; + expect(customLoggerLogSpy).toHaveBeenCalled(); + expect(customLoggerLogSpy).toHaveBeenCalledWith(message, context); }); it('should call custom logger "#error()" method', () => { const message = 'random message'; const context = 'RandomContext'; - const customLoggerErrorSpy = sinon.spy(customLogger, 'error'); + const customLoggerErrorSpy = vi.spyOn(customLogger, 'error'); Logger.error(message, context); - expect(customLoggerErrorSpy.called).to.be.true; - expect(customLoggerErrorSpy.calledWith(message, context)).to.be.true; + expect(customLoggerErrorSpy).toHaveBeenCalled(); + expect(customLoggerErrorSpy).toHaveBeenCalledWith(message, context); }); }); }); @@ -406,56 +393,56 @@ describe('Logger', () => { describe('ConsoleLogger', () => { it('should allow setting and resetting of context', () => { const logger = new ConsoleLogger(); - expect(logger['context']).to.be.undefined; + expect(logger['context']).toBeUndefined(); logger.setContext('context'); - expect(logger['context']).to.equal('context'); + expect(logger['context']).toBe('context'); logger.resetContext(); - expect(logger['context']).to.be.undefined; + expect(logger['context']).toBeUndefined(); const loggerWithContext = new ConsoleLogger('context'); - expect(loggerWithContext['context']).to.equal('context'); + expect(loggerWithContext['context']).toBe('context'); loggerWithContext.setContext('other'); - expect(loggerWithContext['context']).to.equal('other'); + expect(loggerWithContext['context']).toBe('other'); loggerWithContext.resetContext(); - expect(loggerWithContext['context']).to.equal('context'); + expect(loggerWithContext['context']).toBe('context'); }); describe('functions for message', () => { - let processStdoutWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; const logger = new ConsoleLogger(); const message = 'Hello World'; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); }); it('works', () => { logger.log(() => message); - expect(processStdoutWriteSpy.calledOnce).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message); + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(message); // Ensure we didn't serialize the function itself. - expect(processStdoutWriteSpy.firstCall.firstArg).not.to.include(' => '); - expect(processStdoutWriteSpy.firstCall.firstArg).not.to.include( + expect(processStdoutWriteSpy.mock.calls[0][0]).not.toContain(' => '); + expect(processStdoutWriteSpy.mock.calls[0][0]).not.toContain( 'function', ); - expect(processStdoutWriteSpy.firstCall.firstArg).not.to.include( + expect(processStdoutWriteSpy.mock.calls[0][0]).not.toContain( 'Function', ); }); }); describe('classes for message', () => { - let processStdoutWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); }); it("should display class's name or empty for anonymous classes", () => { @@ -470,30 +457,34 @@ describe('Logger', () => { } logger.log(Test); - expect(processStdoutWriteSpy.firstCall.firstArg).to.include(''); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include(Test.name); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(''); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain(Test.name); }); }); describe('forceConsole option', () => { - let consoleLogSpy: sinon.SinonSpy; - let consoleErrorSpy: sinon.SinonSpy; - let processStdoutWriteStub: sinon.SinonStub; - let processStderrWriteStub: sinon.SinonStub; + let consoleLogSpy: ReturnType; + let consoleErrorSpy: ReturnType; + let processStdoutWriteStub: ReturnType; + let processStderrWriteStub: ReturnType; beforeEach(() => { // Stub process.stdout.write to prevent actual output and track calls - processStdoutWriteStub = sinon.stub(process.stdout, 'write'); - processStderrWriteStub = sinon.stub(process.stderr, 'write'); - consoleLogSpy = sinon.spy(console, 'log'); - consoleErrorSpy = sinon.spy(console, 'error'); + processStdoutWriteStub = vi + .spyOn(process.stdout, 'write') + .mockImplementation(() => ({}) as any); + processStderrWriteStub = vi + .spyOn(process.stderr, 'write') + .mockImplementation(() => ({}) as any); + consoleLogSpy = vi.spyOn(console, 'log'); + consoleErrorSpy = vi.spyOn(console, 'error'); }); afterEach(() => { - processStdoutWriteStub.restore(); - processStderrWriteStub.restore(); - consoleLogSpy.restore(); - consoleErrorSpy.restore(); + processStdoutWriteStub.mockRestore(); + processStderrWriteStub.mockRestore(); + consoleLogSpy.mockRestore(); + consoleErrorSpy.mockRestore(); }); it('should use console.log instead of process.stdout.write when forceConsole is true', () => { @@ -503,8 +494,8 @@ describe('Logger', () => { logger.log(message); // When forceConsole is true, console.log should be called - expect(consoleLogSpy.called).to.be.true; - expect(consoleLogSpy.firstCall.firstArg).to.include(message); + expect(consoleLogSpy).toHaveBeenCalled(); + expect(consoleLogSpy.mock.calls[0][0]).toContain(message); }); it('should use console.error instead of process.stderr.write when forceConsole is true', () => { @@ -513,8 +504,8 @@ describe('Logger', () => { logger.error(message); - expect(consoleErrorSpy.called).to.be.true; - expect(consoleErrorSpy.firstCall.firstArg).to.include(message); + expect(consoleErrorSpy).toHaveBeenCalled(); + expect(consoleErrorSpy.mock.calls[0][0]).toContain(message); }); it('should use console.error for stack traces when forceConsole is true', () => { @@ -524,9 +515,9 @@ describe('Logger', () => { logger.error(message, stack); - expect(consoleErrorSpy.calledTwice).to.be.true; - expect(consoleErrorSpy.firstCall.firstArg).to.include(message); - expect(consoleErrorSpy.secondCall.firstArg).to.equal(stack); + expect(consoleErrorSpy).toHaveBeenCalledTimes(2); + expect(consoleErrorSpy.mock.calls[0][0]).toContain(message); + expect(consoleErrorSpy.mock.calls[1][0]).toBe(stack); }); it('should use process.stdout.write when forceConsole is false', () => { @@ -535,9 +526,9 @@ describe('Logger', () => { logger.log(message); - expect(processStdoutWriteStub.called).to.be.true; - expect(processStdoutWriteStub.firstCall.firstArg).to.include(message); - expect(consoleLogSpy.called).to.be.false; + expect(processStdoutWriteStub).toHaveBeenCalled(); + expect(processStdoutWriteStub.mock.calls[0][0]).toContain(message); + expect(consoleLogSpy).not.toHaveBeenCalled(); }); it('should work with JSON mode and forceConsole', () => { @@ -546,11 +537,11 @@ describe('Logger', () => { logger.log(message); - expect(consoleLogSpy.called).to.be.true; + expect(consoleLogSpy).toHaveBeenCalled(); - const output = consoleLogSpy.firstCall.firstArg; + const output = consoleLogSpy.mock.calls[0][0]; const json = JSON.parse(output); - expect(json.message).to.equal(message); + expect(json.message).toBe(message); }); }); }); @@ -559,17 +550,17 @@ describe('Logger', () => { describe('when the default logger is used', () => { const logger = new Logger(); - let processStdoutWriteSpy: sinon.SinonSpy; - let processStderrWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; + let processStderrWriteSpy: ReturnType; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); - processStderrWriteSpy = sinon.spy(process.stderr, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); + processStderrWriteSpy = vi.spyOn(process.stderr, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); - processStderrWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); + processStderrWriteSpy.mockRestore(); }); it('should print one message to the console', () => { @@ -578,11 +569,11 @@ describe('Logger', () => { logger.log(message, context); - expect(processStdoutWriteSpy.calledOnce).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print one message without context to the console', () => { @@ -590,8 +581,8 @@ describe('Logger', () => { logger.log(message); - expect(processStdoutWriteSpy.calledOnce).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include(message); + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print multiple messages to the console', () => { @@ -600,27 +591,21 @@ describe('Logger', () => { logger.log(messages[0], messages[1], messages[2], context); - expect(processStdoutWriteSpy.calledThrice).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( + expect(processStdoutWriteSpy).toHaveBeenCalledTimes(3); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( - messages[0], - ); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(messages[0]); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include( + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include( - messages[1], - ); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain(messages[1]); - expect(processStdoutWriteSpy.thirdCall.firstArg).to.include( + expect(processStdoutWriteSpy.mock.calls[2][0]).toContain( `[${context}]`, ); - expect(processStdoutWriteSpy.thirdCall.firstArg).to.include( - messages[2], - ); + expect(processStdoutWriteSpy.mock.calls[2][0]).toContain(messages[2]); }); it('should print one error to the console with context', () => { @@ -629,11 +614,11 @@ describe('Logger', () => { logger.error(message, context); - expect(processStderrWriteSpy.calledOnce).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy).toHaveBeenCalledOnce(); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print one error to the console with stacktrace', () => { @@ -642,12 +627,10 @@ describe('Logger', () => { logger.error(message, stacktrace); - expect(processStderrWriteSpy.calledTwice).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.not.include(`[]`); - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); - expect(processStderrWriteSpy.secondCall.firstArg).to.equal( - stacktrace + '\n', - ); + expect(processStderrWriteSpy).toHaveBeenCalledTimes(2); + expect(processStderrWriteSpy.mock.calls[0][0]).not.toContain(`[]`); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); + expect(processStderrWriteSpy.mock.calls[1][0]).toBe(stacktrace + '\n'); }); it('should print one error without context to the console', () => { @@ -655,8 +638,8 @@ describe('Logger', () => { logger.error(message); - expect(processStderrWriteSpy.calledOnce).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); + expect(processStderrWriteSpy).toHaveBeenCalledOnce(); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); }); it('should print one error with stacktrace and context to the console', () => { @@ -666,16 +649,14 @@ describe('Logger', () => { logger.error(message, stacktrace, context); - expect(processStderrWriteSpy.calledTwice).to.be.true; + expect(processStderrWriteSpy).toHaveBeenCalledTimes(2); - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); - expect(processStderrWriteSpy.secondCall.firstArg).to.equal( - stacktrace + '\n', - ); + expect(processStderrWriteSpy.mock.calls[1][0]).toBe(stacktrace + '\n'); }); it('should print 2 errors and one stacktrace to the console', () => { @@ -685,25 +666,21 @@ describe('Logger', () => { logger.error(messages[0], messages[1], stack, context); - expect(processStderrWriteSpy.calledThrice).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy).toHaveBeenCalledTimes(3); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include( - messages[0], - ); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(messages[0]); - expect(processStderrWriteSpy.secondCall.firstArg).to.include( + expect(processStderrWriteSpy.mock.calls[1][0]).toContain( `[${context}]`, ); - expect(processStderrWriteSpy.secondCall.firstArg).to.include( - messages[1], - ); + expect(processStderrWriteSpy.mock.calls[1][0]).toContain(messages[1]); - expect(processStderrWriteSpy.thirdCall.firstArg).to.not.include( + expect(processStderrWriteSpy.mock.calls[2][0]).not.toContain( `[${context}]`, ); - expect(processStderrWriteSpy.thirdCall.firstArg).to.equal(stack + '\n'); + expect(processStderrWriteSpy.mock.calls[2][0]).toBe(stack + '\n'); }); }); @@ -711,17 +688,17 @@ describe('Logger', () => { const globalContext = 'GlobalContext'; const logger = new Logger(globalContext, { timestamp: true }); - let processStdoutWriteSpy: sinon.SinonSpy; - let processStderrWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; + let processStderrWriteSpy: ReturnType; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); - processStderrWriteSpy = sinon.spy(process.stderr, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); + processStderrWriteSpy = vi.spyOn(process.stderr, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); - processStderrWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); + processStderrWriteSpy.mockRestore(); }); it('should print multiple messages to the console and append global context', () => { @@ -729,51 +706,45 @@ describe('Logger', () => { logger.log(messages[0], messages[1], messages[2]); - expect(processStdoutWriteSpy.calledThrice).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( + expect(processStdoutWriteSpy).toHaveBeenCalledTimes(3); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( `[${globalContext}]`, ); - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( - messages[0], - ); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain(messages[0]); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include( + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain( `[${globalContext}]`, ); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include( - messages[1], - ); - expect(processStdoutWriteSpy.secondCall.firstArg).to.include('ms'); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain(messages[1]); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain('ms'); - expect(processStdoutWriteSpy.thirdCall.firstArg).to.include( + expect(processStdoutWriteSpy.mock.calls[2][0]).toContain( `[${globalContext}]`, ); - expect(processStdoutWriteSpy.thirdCall.firstArg).to.include( - messages[2], - ); - expect(processStdoutWriteSpy.thirdCall.firstArg).to.include('ms'); + expect(processStdoutWriteSpy.mock.calls[2][0]).toContain(messages[2]); + expect(processStdoutWriteSpy.mock.calls[2][0]).toContain('ms'); }); it('should log out an error to stderr but not include an undefined log', () => { const message = 'message 1'; logger.error(message); - expect(processStderrWriteSpy.calledOnce).to.be.true; - expect(processStderrWriteSpy.firstCall.firstArg).to.include( + expect(processStderrWriteSpy).toHaveBeenCalledOnce(); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain( `[${globalContext}]`, ); - expect(processStderrWriteSpy.firstCall.firstArg).to.include(message); + expect(processStderrWriteSpy.mock.calls[0][0]).toContain(message); }); }); describe('when logging is disabled', () => { const logger = new Logger(); - let processStdoutWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; let previousLoggerRef: LoggerService; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); previousLoggerRef = Logger['localInstanceRef'] || Logger['staticInstanceRef']; @@ -781,7 +752,7 @@ describe('Logger', () => { }); afterEach(() => { - processStdoutWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); Logger.overrideLogger(previousLoggerRef); }); @@ -792,7 +763,7 @@ describe('Logger', () => { logger.log(message, context); - expect(processStdoutWriteSpy.called).to.be.false; + expect(processStdoutWriteSpy).not.toHaveBeenCalled(); }); }); @@ -823,25 +794,29 @@ describe('Logger', () => { it('should call custom logger "#log()" method with context as second argument', () => { const message = 'random log message with global context'; - const customLoggerLogSpy = sinon.spy(customLogger, 'log'); + const customLoggerLogSpy = vi.spyOn(customLogger, 'log'); originalLogger.log(message); - expect(customLoggerLogSpy.called).to.be.true; - expect(customLoggerLogSpy.calledWith(message, globalContext)).to.be - .true; + expect(customLoggerLogSpy).toHaveBeenCalled(); + expect(customLoggerLogSpy).toHaveBeenCalledWith( + message, + globalContext, + ); }); it('should call custom logger "#error()" method with context as third argument', () => { const message = 'random error message with global context'; - const customLoggerErrorSpy = sinon.spy(customLogger, 'error'); + const customLoggerErrorSpy = vi.spyOn(customLogger, 'error'); originalLogger.error(message); - expect(customLoggerErrorSpy.called).to.be.true; - expect( - customLoggerErrorSpy.calledWith(message, undefined, globalContext), - ).to.be.true; + expect(customLoggerErrorSpy).toHaveBeenCalled(); + expect(customLoggerErrorSpy).toHaveBeenCalledWith( + message, + undefined, + globalContext, + ); }); }); describe('without global context', () => { @@ -864,37 +839,40 @@ describe('Logger', () => { const message = 'random message'; const context = 'RandomContext'; - const customLoggerLogSpy = sinon.spy(customLogger, 'log'); + const customLoggerLogSpy = vi.spyOn(customLogger, 'log'); originalLogger.log(message, context); - expect(customLoggerLogSpy.called).to.be.true; - expect(customLoggerLogSpy.calledWith(message, context)).to.be.true; + expect(customLoggerLogSpy).toHaveBeenCalled(); + expect(customLoggerLogSpy).toHaveBeenCalledWith(message, context); }); it('should call custom logger "#error()" method', () => { const message = 'random message'; const context = 'RandomContext'; - const customLoggerErrorSpy = sinon.spy(customLogger, 'error'); + const customLoggerErrorSpy = vi.spyOn(customLogger, 'error'); originalLogger.error(message, undefined, context); - expect(customLoggerErrorSpy.called).to.be.true; - expect(customLoggerErrorSpy.calledWith(message, undefined, context)) - .to.be.true; + expect(customLoggerErrorSpy).toHaveBeenCalled(); + expect(customLoggerErrorSpy).toHaveBeenCalledWith( + message, + undefined, + context, + ); }); }); }); }); describe('ConsoleLogger', () => { - let processStdoutWriteSpy: sinon.SinonSpy; + let processStdoutWriteSpy: ReturnType; beforeEach(() => { - processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); }); afterEach(() => { - processStdoutWriteSpy.restore(); + processStdoutWriteSpy.mockRestore(); }); it('should respect maxStringLength when set to 0', () => { @@ -906,8 +884,8 @@ describe('Logger', () => { consoleLogger.log({ name: 'abcdef' }); - expect(processStdoutWriteSpy.calledOnce).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( "''... 6 more characters", ); }); @@ -921,8 +899,8 @@ describe('Logger', () => { consoleLogger.log({ items: ['a', 'b', 'c'] }); - expect(processStdoutWriteSpy.calledOnce).to.be.true; - expect(processStdoutWriteSpy.firstCall.firstArg).to.include( + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( '... 3 more items', ); }); @@ -944,7 +922,7 @@ describe('Logger', () => { const consoleLogger = new CustomConsoleLogger(); consoleLogger.debug('test'); - expect(processStdoutWriteSpy.firstCall.firstArg).to.equal(`Prefix: test`); + expect(processStdoutWriteSpy.mock.calls[0][0]).toBe(`Prefix: test`); }); it('should support custom formatter and colorizer', () => { @@ -969,12 +947,10 @@ describe('Logger', () => { const consoleLogger = new CustomConsoleLogger(); consoleLogger.debug('test'); - expect(processStdoutWriteSpy.firstCall.firstArg).to.equal( - `Prefix: ~~~test~~~`, - ); + expect(processStdoutWriteSpy.mock.calls[0][0]).toBe(`Prefix: ~~~test~~~`); }); - it('should stringify messages', () => { + it('should stringify messages (plain objects extracted as params)', () => { class CustomConsoleLogger extends ConsoleLogger { protected colorize(message: string, _: LogLevel): string { return message; @@ -982,10 +958,11 @@ describe('Logger', () => { } const consoleLogger = new CustomConsoleLogger({ colors: false }); - const consoleLoggerSpy = sinon.spy( - consoleLogger, - 'stringifyMessage' as keyof ConsoleLogger, + const consoleLoggerSpy = vi.spyOn( + consoleLogger as any, + 'stringifyMessage', ); + // { key: 'str2' } is now extracted as params, not passed through stringifyMessage consoleLogger.debug( 'str1', { key: 'str2' }, @@ -995,26 +972,224 @@ describe('Logger', () => { 1, ); - expect(consoleLoggerSpy.getCall(0).returnValue).to.equal('str1'); - expect(consoleLoggerSpy.getCall(1).returnValue).to.equal( - `Object(1) { - key: 'str2' -}`, - ); - expect(consoleLoggerSpy.getCall(2).returnValue).to.equal( + // stringifyMessage is called for: 'str1', ['str3'], [{ key: 'str4' }], null, 1 + // { key: 'str2' } is extracted as params + expect(consoleLoggerSpy.mock.results[0].value).toBe('str1'); + expect(consoleLoggerSpy.mock.results[1].value).toBe( `Array(1) [ 'str3' ]`, ); - expect(consoleLoggerSpy.getCall(3).returnValue).to.equal( + expect(consoleLoggerSpy.mock.results[2].value).toBe( `Array(1) [ { key: 'str4' } ]`, ); - expect(consoleLoggerSpy.getCall(4).returnValue).to.equal('null'); - expect(consoleLoggerSpy.getCall(5).returnValue).to.equal('1'); + expect(consoleLoggerSpy.mock.results[3].value).toBe('null'); + expect(consoleLoggerSpy.mock.results[4].value).toBe('1'); + }); + }); + + describe('ConsoleLogger - structured logging params', () => { + let processStdoutWriteSpy: ReturnType; + let processStderrWriteSpy: ReturnType; + + beforeEach(() => { + processStdoutWriteSpy = vi.spyOn(process.stdout, 'write'); + processStderrWriteSpy = vi.spyOn(process.stderr, 'write'); + }); + afterEach(() => { + processStdoutWriteSpy.mockRestore(); + processStderrWriteSpy.mockRestore(); + }); + + describe('text mode', () => { + class DeterministicConsoleLogger extends ConsoleLogger { + protected formatPid() { + return '[Nest] 123 - '; + } + + protected getTimestamp(): string { + return '01/01/2026, 12:00:00 AM'; + } + + protected updateAndGetTimestampDiff(): string { + return ''; + } + } + + it('should inline single plain object as params after message', () => { + const logger = new DeterministicConsoleLogger({ colors: false }); + logger.log('User created', { userId: 1 }); + + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toBe( + '[Nest] 123 - 01/01/2026, 12:00:00 AM LOG User created { userId: 1 }\n', + ); + }); + + it('should merge multiple plain objects into single params', () => { + const logger = new DeterministicConsoleLogger({ colors: false }); + logger.log('Request', { userId: 1 }, { reqId: 'abc' }); + + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + expect(processStdoutWriteSpy.mock.calls[0][0]).toBe( + "[Nest] 123 - 01/01/2026, 12:00:00 AM LOG Request { userId: 1, reqId: 'abc' }\n", + ); + }); + + it('should treat plain object as first arg as message, not params', () => { + const logger = new ConsoleLogger({ colors: false }); + logger.log({ randomError: true }); + + expect(processStdoutWriteSpy).toHaveBeenCalledOnce(); + const output = processStdoutWriteSpy.mock.calls[0][0]; + expect(output).toContain('Object(1)'); + expect(output).toContain('randomError: true'); + }); + + it('should keep strings as messages and extract objects as params', () => { + const logger = new DeterministicConsoleLogger({ colors: false }); + logger.log('msg1', 'msg2', { meta: true }, 'Context'); + + // msg1 and msg2 are messages, { meta: true } is params, 'Context' is context + expect(processStdoutWriteSpy).toHaveBeenCalledTimes(2); + expect(processStdoutWriteSpy.mock.calls[0][0]).toBe( + '[Nest] 123 - 01/01/2026, 12:00:00 AM LOG [Context] msg1 { meta: true }\n', + ); + expect(processStdoutWriteSpy.mock.calls[1][0]).toBe( + '[Nest] 123 - 01/01/2026, 12:00:00 AM LOG [Context] msg2 { meta: true }\n', + ); + }); + + it('should not treat arrays as params', () => { + const logger = new ConsoleLogger({ colors: false }); + logger.log('msg', [1, 2, 3]); + + // Array stays as a separate message, not params + expect(processStdoutWriteSpy).toHaveBeenCalledTimes(2); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain('msg'); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain('Array(3)'); + }); + }); + + describe('JSON mode', () => { + it('should include params under params key', () => { + const logger = new ConsoleLogger({ json: true }); + logger.log('User created', { userId: 1 }, 'UserService'); + + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0][0]); + expect(json.message).toBe('User created'); + expect(json.context).toBe('UserService'); + expect(json.params).toEqual({ userId: 1 }); + }); + + it('should not include params key when no objects are passed', () => { + const logger = new ConsoleLogger({ json: true }); + logger.log('simple message'); + + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0][0]); + expect(json.message).toBe('simple message'); + expect(json.params).toBeUndefined(); + }); + + it('should include params, stack, and context for error', () => { + const logger = new ConsoleLogger({ json: true }); + const stack = 'Error: test\n at :1:1'; + logger.error('fail', { reqId: 'abc' }, stack, 'AppService'); + + const json = JSON.parse(processStderrWriteSpy.mock.calls[0][0]); + expect(json.message).toBe('fail'); + expect(json.context).toBe('AppService'); + expect(json.stack).toBe(stack); + expect(json.params).toEqual({ reqId: 'abc' }); + }); + + it('should keep reserved keys nested under params by default', () => { + const logger = new ConsoleLogger({ json: true }); + logger.log( + 'User created', + { message: 'override', level: 'debug' }, + 'UserService', + ); + + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0][0]); + expect(json.level).toBe('log'); + expect(json.message).toBe('User created'); + expect(json.context).toBe('UserService'); + expect(json.params).toEqual({ message: 'override', level: 'debug' }); + }); + + it('should flatten params to root when flattenParams is true', () => { + const logger = new ConsoleLogger({ json: true, flattenParams: true }); + logger.log('User created', { userId: 1, action: 'create' }, 'Svc'); + + const json = JSON.parse(processStdoutWriteSpy.mock.calls[0][0]); + expect(json.message).toBe('User created'); + expect(json.context).toBe('Svc'); + expect(json.userId).toBe(1); + expect(json.action).toBe('create'); + expect(json.params).toBeUndefined(); + }); + + it('should handle error with params but no stack', () => { + const logger = new ConsoleLogger({ json: true }); + logger.error('fail', { reqId: 'abc' }, 'AppService'); + + const json = JSON.parse(processStderrWriteSpy.mock.calls[0][0]); + expect(json.message).toBe('fail'); + expect(json.context).toBe('AppService'); + expect(json.params).toEqual({ reqId: 'abc' }); + expect(json.stack).toBeUndefined(); + }); + + it('should treat trailing stack-like string as stack when params are present', () => { + const logger = new ConsoleLogger({ json: true }); + const stack = 'Error: test\n at AppService.run (app.ts:1:1)'; + logger.error('fail', { reqId: 'abc' }, stack); + + const json = JSON.parse(processStderrWriteSpy.mock.calls[0][0]); + expect(json.message).toBe('fail'); + expect(json.context).toBeUndefined(); + expect(json.params).toEqual({ reqId: 'abc' }); + expect(json.stack).toBe(stack); + }); + }); + + describe('structuredParams: false (legacy behavior)', () => { + it('should treat plain objects as separate messages in text mode', () => { + const logger = new ConsoleLogger({ + colors: false, + structuredParams: false, + }); + logger.log('User created', { userId: 1 }); + + // Two write calls: one for the message, one for the object + expect(processStdoutWriteSpy).toHaveBeenCalledTimes(2); + expect(processStdoutWriteSpy.mock.calls[0][0]).toContain( + 'User created', + ); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain('Object(1)'); + expect(processStdoutWriteSpy.mock.calls[1][0]).toContain('userId: 1'); + }); + + it('should treat plain objects as separate JSON entries', () => { + const logger = new ConsoleLogger({ + json: true, + structuredParams: false, + }); + logger.log('User created', { userId: 1 }); + + // Two JSON lines: one for the string, one for the object + expect(processStdoutWriteSpy).toHaveBeenCalledTimes(2); + const json1 = JSON.parse(processStdoutWriteSpy.mock.calls[0][0]); + const json2 = JSON.parse(processStdoutWriteSpy.mock.calls[1][0]); + expect(json1.message).toBe('User created'); + expect(json1.params).toBeUndefined(); + expect(json2.message).toEqual({ userId: 1 }); + }); }); }); }); diff --git a/packages/common/test/services/utils/filter-log-levels.util.spec.ts b/packages/common/test/services/utils/filter-log-levels.util.spec.ts index d959a99a287..5338ee32b87 100644 --- a/packages/common/test/services/utils/filter-log-levels.util.spec.ts +++ b/packages/common/test/services/utils/filter-log-levels.util.spec.ts @@ -1,30 +1,29 @@ -import { expect } from 'chai'; -import { filterLogLevels } from '../../../services/utils/filter-log-levels.util'; +import { filterLogLevels } from '../../../services/utils/filter-log-levels.util.js'; describe('filterLogLevels', () => { it('should correctly parse an exclusive range', () => { const returned = filterLogLevels('>warn'); - expect(returned).to.deep.equal(['error', 'fatal']); + expect(returned).toEqual(['error', 'fatal']); }); it('should correctly parse an inclusive range', () => { const returned = filterLogLevels('>=warn'); - expect(returned).to.deep.equal(['warn', 'error', 'fatal']); + expect(returned).toEqual(['warn', 'error', 'fatal']); }); it('should correctly parse a string list', () => { const returned = filterLogLevels('verbose,warn,fatal'); - expect(returned).to.deep.equal(['verbose', 'warn', 'fatal']); + expect(returned).toEqual(['verbose', 'warn', 'fatal']); }); it('should correctly parse a single log level', () => { const returned = filterLogLevels('debug'); - expect(returned).to.deep.equal(['debug']); + expect(returned).toEqual(['debug']); }); it('should return all otherwise', () => { const returned = filterLogLevels(); - expect(returned).to.deep.equal([ + expect(returned).toEqual([ 'verbose', 'debug', 'log', diff --git a/packages/common/test/services/utils/is-log-level-enabled.util.spec.ts b/packages/common/test/services/utils/is-log-level-enabled.util.spec.ts index 27d18ea13aa..38914df1fde 100644 --- a/packages/common/test/services/utils/is-log-level-enabled.util.spec.ts +++ b/packages/common/test/services/utils/is-log-level-enabled.util.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { LogLevel } from '../../../services/logger.service'; -import { isLogLevelEnabled } from '../../../services/utils'; +import { LogLevel } from '../../../services/logger.service.js'; +import { isLogLevelEnabled } from '../../../services/utils/index.js'; describe('isLogLevelEnabled', () => { const tests = [ @@ -27,7 +26,7 @@ describe('isLogLevelEnabled', () => { it('should return true', () => { expect( isLogLevelEnabled(...(inputArgs as [LogLevel, LogLevel[]])), - ).to.equal(expectedReturnValue); + ).toBe(expectedReturnValue); }); }); }); @@ -35,7 +34,7 @@ describe('isLogLevelEnabled', () => { describe(`when log levels = undefined`, () => { it('should return false', () => { - expect(isLogLevelEnabled('warn', undefined)).to.be.false; + expect(isLogLevelEnabled('warn', undefined)).toBe(false); }); }); }); diff --git a/packages/common/test/utils/cli-colors.util.spec.ts b/packages/common/test/utils/cli-colors.util.spec.ts new file mode 100644 index 00000000000..4e5df480d48 --- /dev/null +++ b/packages/common/test/utils/cli-colors.util.spec.ts @@ -0,0 +1,102 @@ +import { clc, isColorAllowed, yellow } from '../../utils/cli-colors.util.js'; + +describe('cli-colors.util', () => { + const originalEnv = process.env.NO_COLOR; + + afterEach(() => { + if (originalEnv === undefined) { + delete process.env.NO_COLOR; + } else { + process.env.NO_COLOR = originalEnv; + } + }); + + describe('isColorAllowed', () => { + it('should return true when NO_COLOR is not set', () => { + delete process.env.NO_COLOR; + expect(isColorAllowed()).toBe(true); + }); + + it('should return false when NO_COLOR is set', () => { + process.env.NO_COLOR = '1'; + expect(isColorAllowed()).toBe(false); + }); + + it('should return true when NO_COLOR is an empty string', () => { + process.env.NO_COLOR = ''; + expect(isColorAllowed()).toBe(true); + }); + }); + + describe('clc (color enabled)', () => { + beforeEach(() => { + delete process.env.NO_COLOR; + }); + + it('should wrap text in bold ANSI codes', () => { + expect(clc.bold('hello')).toBe('\x1B[1mhello\x1B[0m'); + }); + + it('should wrap text in green ANSI codes', () => { + expect(clc.green('hello')).toBe('\x1B[32mhello\x1B[39m'); + }); + + it('should wrap text in yellow ANSI codes', () => { + expect(clc.yellow('hello')).toBe('\x1B[33mhello\x1B[39m'); + }); + + it('should wrap text in red ANSI codes', () => { + expect(clc.red('hello')).toBe('\x1B[31mhello\x1B[39m'); + }); + + it('should wrap text in magentaBright ANSI codes', () => { + expect(clc.magentaBright('hello')).toBe('\x1B[95mhello\x1B[39m'); + }); + + it('should wrap text in cyanBright ANSI codes', () => { + expect(clc.cyanBright('hello')).toBe('\x1B[96mhello\x1B[39m'); + }); + }); + + describe('clc (color disabled)', () => { + beforeEach(() => { + process.env.NO_COLOR = '1'; + }); + + it('should return plain text for bold', () => { + expect(clc.bold('hello')).toBe('hello'); + }); + + it('should return plain text for green', () => { + expect(clc.green('hello')).toBe('hello'); + }); + + it('should return plain text for yellow', () => { + expect(clc.yellow('hello')).toBe('hello'); + }); + + it('should return plain text for red', () => { + expect(clc.red('hello')).toBe('hello'); + }); + + it('should return plain text for magentaBright', () => { + expect(clc.magentaBright('hello')).toBe('hello'); + }); + + it('should return plain text for cyanBright', () => { + expect(clc.cyanBright('hello')).toBe('hello'); + }); + }); + + describe('yellow (standalone)', () => { + it('should use 38;5;3 ANSI code when color is allowed', () => { + delete process.env.NO_COLOR; + expect(yellow('hello')).toBe('\x1B[38;5;3mhello\x1B[39m'); + }); + + it('should return plain text when color is disabled', () => { + process.env.NO_COLOR = '1'; + expect(yellow('hello')).toBe('hello'); + }); + }); +}); diff --git a/packages/common/test/utils/forward-ref.util.spec.ts b/packages/common/test/utils/forward-ref.util.spec.ts index 829b3b820b0..1ab24cfcdd5 100644 --- a/packages/common/test/utils/forward-ref.util.spec.ts +++ b/packages/common/test/utils/forward-ref.util.spec.ts @@ -1,10 +1,9 @@ -import { expect } from 'chai'; -import { forwardRef } from '../../utils/forward-ref.util'; +import { forwardRef } from '../../utils/forward-ref.util.js'; describe('forwardRef', () => { it('should return object with forwardRef property', () => { const fn = () => ({}); const referenceFn = forwardRef(() => fn); - expect(referenceFn.forwardRef()).to.be.eql(fn); + expect(referenceFn.forwardRef()).toEqual(fn); }); }); diff --git a/packages/common/test/utils/load-package.util.spec.ts b/packages/common/test/utils/load-package.util.spec.ts index 3126cb3b64f..1fb38dd57f4 100644 --- a/packages/common/test/utils/load-package.util.spec.ts +++ b/packages/common/test/utils/load-package.util.spec.ts @@ -1,12 +1,11 @@ -import { expect } from 'chai'; -import { loadPackage } from '../../utils/load-package.util'; +import { loadPackage } from '../../utils/load-package.util.js'; describe('loadPackage', () => { describe('when package is available', () => { - it('should return package', () => { - expect(loadPackage('reflect-metadata', 'ctx')).to.be.eql( - require('reflect-metadata'), - ); + it('should return package', async () => { + const result = await loadPackage('reflect-metadata', 'ctx'); + const expected = await import('reflect-metadata'); + expect(result).toEqual(expected); }); }); }); diff --git a/packages/common/test/utils/merge-with-values.util.spec.ts b/packages/common/test/utils/merge-with-values.util.spec.ts index 1ecf8be1f8c..dda49fb6bab 100644 --- a/packages/common/test/utils/merge-with-values.util.spec.ts +++ b/packages/common/test/utils/merge-with-values.util.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { MergeWithValues } from '../../utils/merge-with-values.util'; +import { MergeWithValues } from '../../utils/merge-with-values.util.js'; describe('MergeWithValues', () => { let type; @@ -10,9 +9,9 @@ describe('MergeWithValues', () => { type = MergeWithValues(data)(Test); }); it('should enrich prototype with given values', () => { - expect(type.prototype).to.contain(data); + expect(type.prototype).toMatchObject(data); }); it('should set name of metatype', () => { - expect(type.name).to.eq(Test.name + JSON.stringify(data)); + expect(type.name).toBe(Test.name + JSON.stringify(data)); }); }); diff --git a/packages/common/test/utils/random-string-generator.util.spec.ts b/packages/common/test/utils/random-string-generator.util.spec.ts index f3aeee3dcf3..82b56f1d658 100644 --- a/packages/common/test/utils/random-string-generator.util.spec.ts +++ b/packages/common/test/utils/random-string-generator.util.spec.ts @@ -1,8 +1,7 @@ -import { expect } from 'chai'; -import { randomStringGenerator } from '../../utils/random-string-generator.util'; +import { randomStringGenerator } from '../../utils/random-string-generator.util.js'; describe('randomStringGenerator', () => { it('should generate random string', () => { - expect(randomStringGenerator()).to.be.a('string'); + expect(randomStringGenerator()).toBeTypeOf('string'); }); }); diff --git a/packages/common/test/utils/select-exception-filter-metadata.util.spec.ts b/packages/common/test/utils/select-exception-filter-metadata.util.spec.ts index f2fb5b92fa8..9dec1c92f16 100644 --- a/packages/common/test/utils/select-exception-filter-metadata.util.spec.ts +++ b/packages/common/test/utils/select-exception-filter-metadata.util.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { selectExceptionFilterMetadata } from '../../utils/select-exception-filter-metadata.util'; +import { selectExceptionFilterMetadata } from '../../utils/select-exception-filter-metadata.util.js'; class FirstError {} @@ -22,9 +21,9 @@ describe('selectExceptionFilterMetadata', () => { }, ]; - expect( - selectExceptionFilterMetadata(metadataList, new FourthError()), - ).to.be.equal(metadataList[1]); + expect(selectExceptionFilterMetadata(metadataList, new FourthError())).toBe( + metadataList[1], + ); }); describe('when multiple exception handlers are accepting error handling', () => { @@ -42,7 +41,7 @@ describe('selectExceptionFilterMetadata', () => { expect( selectExceptionFilterMetadata(metadataList, new FirstError()), - ).to.be.equal(metadataList[0]); + ).toBe(metadataList[0]); }); }); @@ -79,7 +78,7 @@ describe('selectExceptionFilterMetadata', () => { expect( selectExceptionFilterMetadata(metadataList, new ThirdError()), - ).to.be.equal(metadataList[1]); + ).toBe(metadataList[1]); }); }); }); diff --git a/packages/common/test/utils/shared.utils.spec.ts b/packages/common/test/utils/shared.utils.spec.ts index d7c0de0eac3..d9ab2f02389 100644 --- a/packages/common/test/utils/shared.utils.spec.ts +++ b/packages/common/test/utils/shared.utils.spec.ts @@ -1,8 +1,8 @@ -import { expect } from 'chai'; import { addLeadingSlash, isConstructor, isEmpty, + isEmptyArray, isFunction, isNil, isNumber, @@ -13,7 +13,7 @@ import { isUndefined, normalizePath, stripEndSlash, -} from '../../utils/shared.utils'; +} from '../../utils/shared.utils.js'; function Foo(a) { this.a = 1; @@ -22,153 +22,238 @@ function Foo(a) { describe('Shared utils', () => { describe('isUndefined', () => { it('should return true when obj is undefined', () => { - expect(isUndefined(undefined)).to.be.true; + expect(isUndefined(undefined)).toBe(true); }); it('should return false when object is not undefined', () => { - expect(isUndefined({})).to.be.false; + expect(isUndefined({})).toBe(false); + }); + it('should return false for falsy values like false, 0, or empty string', () => { + expect(isUndefined(false)).to.be.false; + expect(isUndefined(0)).to.be.false; + expect(isUndefined('')).to.be.false; }); }); + describe('isFunction', () => { it('should return true when obj is function', () => { - expect(isFunction(() => ({}))).to.be.true; + expect(isFunction(() => ({}))).toBe(true); }); it('should return false when object is not function', () => { - expect(isFunction(null)).to.be.false; - expect(isFunction(undefined)).to.be.false; + expect(isFunction(null)).toBe(false); + expect(isFunction(undefined)).toBe(false); }); }); + describe('isObject', () => { it('should return true when obj is object', () => { - expect(isObject({})).to.be.true; + expect(isObject({})).toBe(true); }); + it('should return false when object is not object', () => { - expect(isObject(3)).to.be.false; - expect(isObject(null)).to.be.false; - expect(isObject(undefined)).to.be.false; + expect(isObject(3)).toBe(false); + expect(isObject(null)).toBe(false); + expect(isObject(undefined)).toBe(false); }); }); + describe('isPlainObject', () => { it('should return true when obj is plain object', () => { - expect(isPlainObject({})).to.be.true; - expect(isPlainObject({ prop: true })).to.be.true; + expect(isPlainObject({})).toBe(true); + expect(isPlainObject({ prop: true })).toBe(true); expect( isPlainObject({ constructor: Foo, }), - ).to.be.true; - expect(isPlainObject(Object.create(null))).to.be.true; + ).toBe(true); + expect(isPlainObject(Object.create(null))).toBe(true); }); it('should return false when object is not object', () => { - expect(isPlainObject(3)).to.be.false; - expect(isPlainObject(null)).to.be.false; - expect(isPlainObject(undefined)).to.be.false; - expect(isPlainObject([1, 2, 3])).to.be.false; - expect(isPlainObject(new Date())).to.be.false; - expect(isPlainObject(new Foo(1))).to.be.false; + expect(isPlainObject(3)).toBe(false); + expect(isPlainObject(null)).toBe(false); + expect(isPlainObject(undefined)).toBe(false); + expect(isPlainObject([1, 2, 3])).toBe(false); + expect(isPlainObject(new Date())).toBe(false); + expect(isPlainObject(new Foo(1))).toBe(false); + }); + it('should return false for objects with custom prototypes', () => { + function CustomObject() {} + expect(isPlainObject(new CustomObject())).to.be.false; }); }); + describe('isString', () => { it('should return true when val is a string', () => { - expect(isString('true')).to.be.true; + expect(isString('true')).toBe(true); }); it('should return false when val is not a string', () => { - expect(isString(new String('fine'))).to.be.false; - expect(isString(false)).to.be.false; - expect(isString(null)).to.be.false; - expect(isString(undefined)).to.be.false; + expect(isString(new String('fine'))).toBe(false); + expect(isString(false)).toBe(false); + expect(isString(null)).toBe(false); + expect(isString(undefined)).toBe(false); }); }); + describe('isSymbol', () => { it('should return true when val is a Symbol', () => { - expect(isSymbol(Symbol())).to.be.true; + expect(isSymbol(Symbol())).toBe(true); }); it('should return false when val is not a symbol', () => { - expect(isSymbol('Symbol()')).to.be.false; - expect(isSymbol(false)).to.be.false; - expect(isSymbol(null)).to.be.false; - expect(isSymbol(undefined)).to.be.false; + expect(isSymbol('Symbol()')).toBe(false); + expect(isSymbol(false)).toBe(false); + expect(isSymbol(null)).toBe(false); + expect(isSymbol(undefined)).toBe(false); + }); + it('should return false for invalid Symbol objects', () => { + expect(isSymbol(Object(Symbol()))).to.be.false; }); }); + describe('isNumber', () => { it('should return true when val is a number or NaN', () => { - expect(isNumber(1)).to.be.true; - expect(isNumber(1.23)).to.be.true; // with decimals - expect(isNumber(123e-5)).to.be.true; // scientific (exponent) notation - expect(isNumber(0o1)).to.be.true; // octal notation - expect(isNumber(0b1)).to.be.true; // binary notation - expect(isNumber(0x1)).to.be.true; // hexadecimal notation - expect(isNumber(NaN)).to.be.true; + expect(isNumber(1)).toBe(true); + expect(isNumber(1.23)).toBe(true); // with decimals + expect(isNumber(123e-5)).toBe(true); // scientific (exponent) notation + expect(isNumber(0o1)).toBe(true); // octal notation + expect(isNumber(0b1)).toBe(true); // binary notation + expect(isNumber(0x1)).toBe(true); // hexadecimal notation + expect(isNumber(NaN)).toBe(true); + expect(isNumber(Infinity)).toBe(true); + expect(isNumber(-Infinity)).toBe(true); }); it('should return false when val is not a number', () => { - // expect(isNumber(1n)).to.be.false; // big int (available on ES2020) - expect(isNumber('1')).to.be.false; // string - expect(isNumber(undefined)).to.be.false; // nullish - expect(isNumber(null)).to.be.false; // nullish + // expect(isNumber(1n)).toBe(false); // big int (available on ES2020) + expect(isNumber('1')).toBe(false); // string + expect(isNumber(undefined)).toBe(false); // nullish + expect(isNumber(null)).toBe(false); // nullish + expect(isNumber(new Number(123))).toBe(false); // number }); }); + describe('isConstructor', () => { it('should return true when string is equal to constructor', () => { - expect(isConstructor('constructor')).to.be.true; + expect(isConstructor('constructor')).toBe(true); }); it('should return false when string is not equal to constructor', () => { - expect(isConstructor('nope')).to.be.false; + expect(isConstructor('nope')).toBe(false); + }); + it('should return false for non-string values', () => { + expect(isConstructor(null)).to.be.false; + expect(isConstructor(undefined)).to.be.false; + expect(isConstructor(123)).to.be.false; }); }); + describe('addLeadingSlash', () => { it('should return the validated path ("add / if not exists")', () => { - expect(addLeadingSlash('nope')).to.be.eql('/nope'); - expect(addLeadingSlash('{:nope}')).to.be.eql('/{:nope}'); + expect(addLeadingSlash('nope')).toEqual('/nope'); + expect(addLeadingSlash('{:nope}')).toEqual('/{:nope}'); }); it('should return the same path', () => { - expect(addLeadingSlash('/nope')).to.be.eql('/nope'); - expect(addLeadingSlash('{/:nope}')).to.be.eql('{/:nope}'); + expect(addLeadingSlash('/nope')).toEqual('/nope'); + expect(addLeadingSlash('{/:nope}')).toEqual('{/:nope}'); }); it('should return empty path', () => { - expect(addLeadingSlash('')).to.be.eql(''); - expect(addLeadingSlash(null!)).to.be.eql(''); - expect(addLeadingSlash(undefined)).to.be.eql(''); + expect(addLeadingSlash('')).toEqual(''); + expect(addLeadingSlash(null!)).toEqual(''); + expect(addLeadingSlash(undefined)).toEqual(''); + }); + it('should handle paths with special characters', () => { + expect(addLeadingSlash('path-with-special-chars!@#$%^&*()')).to.eql( + '/path-with-special-chars!@#$%^&*()', + ); }); }); + describe('normalizePath', () => { it('should remove all trailing slashes at the end of the path', () => { - expect(normalizePath('path/')).to.be.eql('/path'); - expect(normalizePath('path///')).to.be.eql('/path'); - expect(normalizePath('/path/path///')).to.be.eql('/path/path'); + expect(normalizePath('path/')).toEqual('/path'); + expect(normalizePath('path///')).toEqual('/path'); + expect(normalizePath('/path/path///')).toEqual('/path/path'); }); it('should replace all slashes with only one slash', () => { - expect(normalizePath('////path/')).to.be.eql('/path'); - expect(normalizePath('///')).to.be.eql('/'); - expect(normalizePath('/path////path///')).to.be.eql('/path/path'); + expect(normalizePath('////path/')).toEqual('/path'); + expect(normalizePath('///')).toEqual('/'); + expect(normalizePath('/path////path///')).toEqual('/path/path'); }); it('should return / for empty path', () => { - expect(normalizePath('')).to.be.eql('/'); - expect(normalizePath(null!)).to.be.eql('/'); - expect(normalizePath(undefined)).to.be.eql('/'); + expect(normalizePath('')).toEqual('/'); + expect(normalizePath(null!)).toEqual('/'); + expect(normalizePath(undefined)).toEqual('/'); }); }); + describe('isNil', () => { it('should return true when obj is undefined or null', () => { - expect(isNil(undefined)).to.be.true; - expect(isNil(null)).to.be.true; + expect(isNil(undefined)).toBe(true); + expect(isNil(null)).toBe(true); }); it('should return false when object is not undefined and null', () => { - expect(isNil('3')).to.be.false; + expect(isNil('3')).toBe(false); + }); + it('should return false for falsy values like false, 0, or empty string', () => { + expect(isNil(false)).to.be.false; + expect(isNil(0)).to.be.false; + expect(isNil('')).to.be.false; }); }); + describe('isEmpty', () => { it('should return true when array is empty or not exists', () => { expect(isEmpty([])).to.be.true; - expect(isEmpty(null)).to.be.true; - expect(isEmpty(undefined)).to.be.true; }); + it('should return false when array is not empty', () => { expect(isEmpty([1, 2])).to.be.false; }); + it('should return false for non-array values', () => { + expect(isEmpty({})).to.be.false; + expect(isEmpty('')).to.be.false; + expect(isEmpty(0)).to.be.false; + expect(isEmpty(false)).to.be.false; + expect(isEmpty(Symbol())).to.be.false; + expect(isEmpty(() => {})).to.be.false; + }); }); + + describe('isEmptyArray', () => { + it('should return true when array is empty', () => { + expect(isEmptyArray([])).toBe(true); + }); + + it('should return false when array is not empty', () => { + expect(isEmptyArray([1, 2])).toBe(false); + expect(isEmptyArray(['a', 'b', 'c'])).toBe(false); + expect(isEmptyArray([{}])).toBe(false); + }); + + it('should return false for non-array values', () => { + expect(isEmptyArray(null)).toBe(false); + expect(isEmptyArray(undefined)).toBe(false); + expect(isEmptyArray({})).toBe(false); + expect(isEmptyArray('')).to.be.false; + expect(isEmptyArray(0)).to.be.false; + expect(isEmptyArray(false)).to.be.false; + expect(isEmptyArray(Symbol())).to.be.false; + expect(isEmptyArray(() => {})).to.be.false; + }); + + it('should return false for array-like objects', () => { + expect(isEmptyArray({ length: 0 })).to.be.false; + expect(isEmptyArray({ length: 1 })).to.be.false; + }); + + it('should return false for sparse arrays', () => { + const sparseArray = new Array(3); + expect(isEmptyArray(sparseArray)).to.be.false; + }); + }); + describe('stripEndSlash', () => { it('should strip end slash if present', () => { - expect(stripEndSlash('/cats/')).to.equal('/cats'); - expect(stripEndSlash('/cats')).to.equal('/cats'); + expect(stripEndSlash('/cats/')).toBe('/cats'); + }); + it('should return the same path if no trailing slash exists', () => { + expect(stripEndSlash('/cats')).toBe('/cats'); }); }); }); diff --git a/packages/common/test/utils/validate-each.util.spec.ts b/packages/common/test/utils/validate-each.util.spec.ts index 58d8cb499d8..706e382ca14 100644 --- a/packages/common/test/utils/validate-each.util.spec.ts +++ b/packages/common/test/utils/validate-each.util.spec.ts @@ -1,22 +1,22 @@ -import { expect } from 'chai'; -import { isFunction } from '../../utils/shared.utils'; +import { isFunction } from '../../utils/shared.utils.js'; import { validateEach, InvalidDecoratorItemException, -} from '../../utils/validate-each.util'; +} from '../../utils/validate-each.util.js'; describe('validateEach', () => { describe('when any item will not pass predicate', () => { it('should throw exception', () => { expect(() => validateEach({ name: 'test' } as any, ['test'], isFunction, '', ''), - ).to.throws(InvalidDecoratorItemException); + ).toThrow(InvalidDecoratorItemException); }); }); describe('when all items passed predicate', () => { it('should return true', () => { - expect(validateEach({} as any, [() => null], isFunction, '', '')).to.be - .true; + expect(validateEach({} as any, [() => null], isFunction, '', '')).toBe( + true, + ); }); }); }); diff --git a/packages/common/tsconfig.build.json b/packages/common/tsconfig.build.json index 2d16c3a204b..bd377880ea5 100644 --- a/packages/common/tsconfig.build.json +++ b/packages/common/tsconfig.build.json @@ -2,8 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": {} + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [] diff --git a/packages/common/utils/assign-custom-metadata.util.ts b/packages/common/utils/assign-custom-metadata.util.ts index 38bd926dbcf..7af46a10fa6 100644 --- a/packages/common/utils/assign-custom-metadata.util.ts +++ b/packages/common/utils/assign-custom-metadata.util.ts @@ -1,10 +1,11 @@ -import { CUSTOM_ROUTE_ARGS_METADATA } from '../constants'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import { CUSTOM_ROUTE_ARGS_METADATA } from '../constants.js'; import { ParamData, RouteParamMetadata, -} from '../decorators/http/route-params.decorator'; -import { PipeTransform, Type } from '../interfaces'; -import { CustomParamFactory } from '../interfaces/features/custom-route-param-factory.interface'; +} from '../decorators/http/route-params.decorator.js'; +import { CustomParamFactory } from '../interfaces/features/custom-route-param-factory.interface.js'; +import { PipeTransform, Type } from '../interfaces/index.js'; export function assignCustomParameterMetadata( args: Record, @@ -12,6 +13,7 @@ export function assignCustomParameterMetadata( index: number, factory: CustomParamFactory, data?: ParamData, + schema?: StandardSchemaV1, ...pipes: (Type | PipeTransform)[] ) { return { @@ -21,6 +23,7 @@ export function assignCustomParameterMetadata( factory, data, pipes, + ...(schema !== undefined && { schema }), }, }; } diff --git a/packages/common/utils/forward-ref.util.ts b/packages/common/utils/forward-ref.util.ts index 1b380c022c9..91f95005444 100644 --- a/packages/common/utils/forward-ref.util.ts +++ b/packages/common/utils/forward-ref.util.ts @@ -1,4 +1,4 @@ -import { ForwardReference } from '../interfaces/modules/forward-reference.interface'; +import { ForwardReference } from '../interfaces/modules/forward-reference.interface.js'; /** * @publicApi diff --git a/packages/common/utils/http-error-by-code.util.ts b/packages/common/utils/http-error-by-code.util.ts index 1fc953be9ea..fc686c4034b 100644 --- a/packages/common/utils/http-error-by-code.util.ts +++ b/packages/common/utils/http-error-by-code.util.ts @@ -1,4 +1,4 @@ -import { HttpStatus } from '../enums'; +import { HttpStatus } from '../enums/index.js'; import { BadGatewayException, BadRequestException, @@ -19,8 +19,8 @@ import { UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, -} from '../exceptions'; -import { Type } from '../interfaces'; +} from '../exceptions/index.js'; +import { Type } from '../interfaces/index.js'; export type ErrorHttpStatusCode = | HttpStatus.BAD_GATEWAY diff --git a/packages/common/utils/index.ts b/packages/common/utils/index.ts index c8719691cd3..3d7c02be905 100644 --- a/packages/common/utils/index.ts +++ b/packages/common/utils/index.ts @@ -1 +1 @@ -export * from './forward-ref.util'; +export * from './forward-ref.util.js'; diff --git a/packages/common/utils/load-package.util.ts b/packages/common/utils/load-package.util.ts index 1e0bafbdb48..a947559e694 100644 --- a/packages/common/utils/load-package.util.ts +++ b/packages/common/utils/load-package.util.ts @@ -1,20 +1,109 @@ -import { Logger } from '../services/logger.service'; +import { createRequire } from 'module'; +import { Logger } from '../services/logger.service.js'; const MISSING_REQUIRED_DEPENDENCY = (name: string, reason: string) => - `The "${name}" package is missing. Please, make sure to install it to take advantage of ${reason}.`; + `The "${name}" package is missing. Please, make sure to install it to use ${reason}.`; const logger = new Logger('PackageLoader'); -export function loadPackage( +/** + * Cache of already-loaded packages keyed by package name. + * Allows subsequent calls (including synchronous ones) to + * return the module without another async import(). + */ +const packageCache = new Map(); + +export async function loadPackage( packageName: string, context: string, loaderFn?: Function, ) { + const cached = packageCache.get(packageName); + if (cached) { + return cached; + } try { - return loaderFn ? loaderFn() : require(packageName); + const pkg = loaderFn ? await loaderFn() : await import(packageName); + packageCache.set(packageName, pkg); + return pkg; } catch (e) { logger.error(MISSING_REQUIRED_DEPENDENCY(packageName, context)); Logger.flush(); process.exit(1); } } + +/** + * Synchronously loads a package using `createRequire` and caches it. + * This is meant for optional dependencies that must be loaded in + * synchronous contexts (e.g. constructors). + * + * @param loaderFn Optional synchronous loader (e.g. + * `() => createRequire(import.meta.url)('pkg')`). + * When provided, bundlers can statically analyse the string literal. + * Falls back to a `createRequire` call resolved from this file. + */ +export function loadPackageSync( + packageName: string, + context: string, + loaderFn?: () => any, +): any { + const cached = packageCache.get(packageName); + if (cached) { + return cached; + } + try { + const pkg = loaderFn + ? loaderFn() + : createRequire(import.meta.url)(packageName); + packageCache.set(packageName, pkg); + return pkg; + } catch (e) { + logger.error(MISSING_REQUIRED_DEPENDENCY(packageName, context)); + Logger.flush(); + process.exit(1); + } +} + +/** + * Synchronously returns a package that was previously loaded and cached + * via {@link loadPackage}. Throws if the package has not been loaded yet. + * + * Use this in methods that must remain synchronous (e.g. `connectMicroservice`). + * Ensure that `loadPackage()` has been `await`ed for the same package name + * before calling this function (typically during `init()` or `compile()`). + */ +export function loadPackageCached(packageName: string): any { + const cached = packageCache.get(packageName); + if (!cached) { + throw new Error( + `Package "${packageName}" has not been loaded yet. ` + + `Ensure loadPackage("${packageName}", ...) has been awaited before calling loadPackageCached.`, + ); + } + return cached; +} + +/** + * Attempts to load and cache a package. Returns the loaded module on success + * or `null` if the package is not installed. + * + * Unlike {@link loadPackage}, this function does **not** terminate the process + * when the package is missing, making it suitable for optional dependencies. + */ +export async function tryLoadPackage( + packageName: string, + loaderFn?: Function, +): Promise { + const cached = packageCache.get(packageName); + if (cached) { + return cached; + } + try { + const pkg = loaderFn ? await loaderFn() : await import(packageName); + packageCache.set(packageName, pkg); + return pkg; + } catch { + return null; + } +} diff --git a/packages/common/utils/select-exception-filter-metadata.util.ts b/packages/common/utils/select-exception-filter-metadata.util.ts index e1e1661f50b..3a6b4294e0f 100644 --- a/packages/common/utils/select-exception-filter-metadata.util.ts +++ b/packages/common/utils/select-exception-filter-metadata.util.ts @@ -1,4 +1,4 @@ -import { ExceptionFilterMetadata } from '../interfaces/exceptions'; +import { ExceptionFilterMetadata } from '../interfaces/exceptions/index.js'; export const selectExceptionFilterMetadata = ( filters: ExceptionFilterMetadata[], diff --git a/packages/common/utils/shared.utils.ts b/packages/common/utils/shared.utils.ts index e936c8d2448..38b7be4f62f 100644 --- a/packages/common/utils/shared.utils.ts +++ b/packages/common/utils/shared.utils.ts @@ -1,10 +1,10 @@ -export const isUndefined = (obj: any): obj is undefined => +export const isUndefined = (obj: unknown): obj is undefined => typeof obj === 'undefined'; -export const isObject = (fn: any): fn is object => +export const isObject = (fn: unknown): fn is object => !isNil(fn) && typeof fn === 'object'; -export const isPlainObject = (fn: any): fn is object => { +export const isPlainObject = (fn: unknown): fn is object => { if (!isObject(fn)) { return false; } @@ -37,15 +37,39 @@ export const normalizePath = (path?: string): string => : '/' + path.replace(/\/+$/, '') : '/'; -export const stripEndSlash = (path: string) => - path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path; +export const stripEndSlash = (path: string): string => + path.endsWith('/') ? path.slice(0, -1) : path; -export const isFunction = (val: any): val is Function => +export const isFunction = (val: unknown): val is Function => typeof val === 'function'; -export const isString = (val: any): val is string => typeof val === 'string'; -export const isNumber = (val: any): val is number => typeof val === 'number'; -export const isConstructor = (val: any): boolean => val === 'constructor'; -export const isNil = (val: any): val is null | undefined => + +export const isString = (val: unknown): val is string => + typeof val === 'string'; + +export const isNumber = (val: unknown): val is number => + typeof val === 'number'; + +export const isConstructor = (val: unknown): boolean => val === 'constructor'; + +export const isNil = (val: unknown): val is null | undefined => isUndefined(val) || val === null; -export const isEmpty = (array: any): boolean => !(array && array.length > 0); -export const isSymbol = (val: any): val is symbol => typeof val === 'symbol'; + +export const isEmpty = (value: unknown): boolean => { + if (isNil(value)) { + return true; + } + if (Array.isArray(value)) { + return value.length === 0; + } + return false; +}; + +export const isEmptyArray = (array: unknown): boolean => { + if (!Array.isArray(array)) { + return false; + } + return array.length === 0; +}; + +export const isSymbol = (val: unknown): val is symbol => + typeof val === 'symbol'; diff --git a/packages/common/utils/validate-module-keys.util.ts b/packages/common/utils/validate-module-keys.util.ts index ade58abd05f..e6996f42833 100644 --- a/packages/common/utils/validate-module-keys.util.ts +++ b/packages/common/utils/validate-module-keys.util.ts @@ -1,4 +1,4 @@ -import { MODULE_METADATA as metadataConstants } from '../constants'; +import { MODULE_METADATA as metadataConstants } from '../constants.js'; export const INVALID_MODULE_CONFIG_MESSAGE = ( text: TemplateStringsArray, diff --git a/packages/core/adapters/http-adapter.ts b/packages/core/adapters/http-adapter.ts index 5c4119218b2..dcc3c187ea3 100644 --- a/packages/core/adapters/http-adapter.ts +++ b/packages/core/adapters/http-adapter.ts @@ -1,6 +1,10 @@ -import { HttpServer, RequestMethod, VersioningOptions } from '@nestjs/common'; -import { RequestHandler, VersionValue } from '@nestjs/common/interfaces'; -import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; +import type { + HttpServer, + RequestMethod, + VersioningOptions, +} from '@nestjs/common'; +import type { RequestHandler, VersionValue } from '@nestjs/common/internal'; +import type { NestApplicationOptions } from '@nestjs/common'; /** * @publicApi @@ -159,6 +163,12 @@ export abstract class AbstractHttpAdapter< public setOnResponseHook(onResponseHook: Function): void {} + public beforeClose(): void {} + + public mapException(error: unknown): unknown { + return error; + } + abstract close(); abstract initHttpServer(options: NestApplicationOptions); abstract useStaticAssets(...args: any[]); diff --git a/packages/core/adapters/index.ts b/packages/core/adapters/index.ts index 8063987a060..d51859f7a9c 100644 --- a/packages/core/adapters/index.ts +++ b/packages/core/adapters/index.ts @@ -1 +1 @@ -export * from './http-adapter'; +export * from './http-adapter.js'; diff --git a/packages/core/application-config.ts b/packages/core/application-config.ts index e023e08060e..a3a9a54d5ba 100644 --- a/packages/core/application-config.ts +++ b/packages/core/application-config.ts @@ -1,14 +1,15 @@ -import { +import type { CanActivate, ExceptionFilter, NestInterceptor, PipeTransform, + PreRequestHook, VersioningOptions, WebSocketAdapter, } from '@nestjs/common'; -import { GlobalPrefixOptions } from '@nestjs/common/interfaces'; -import { InstanceWrapper } from './injector/instance-wrapper'; -import { ExcludeRouteMetadata } from './router/interfaces/exclude-route-metadata.interface'; +import { InstanceWrapper } from './injector/instance-wrapper.js'; +import { ExcludeRouteMetadata } from './router/interfaces/exclude-route-metadata.interface.js'; +import type { GlobalPrefixOptions } from '@nestjs/common/internal'; export class ApplicationConfig { private globalPrefix = ''; @@ -17,6 +18,7 @@ export class ApplicationConfig { private globalFilters: Array = []; private globalInterceptors: Array = []; private globalGuards: Array = []; + private globalPreRequestHooks: Array = []; private versioningOptions: VersioningOptions; private readonly globalRequestPipes: InstanceWrapper[] = []; private readonly globalRequestFilters: InstanceWrapper[] = @@ -135,6 +137,14 @@ export class ApplicationConfig { return this.globalRequestGuards; } + public registerPreRequestHook(...hooks: PreRequestHook[]) { + this.globalPreRequestHooks = this.globalPreRequestHooks.concat(hooks); + } + + public getGlobalPreRequestHooks(): PreRequestHook[] { + return this.globalPreRequestHooks; + } + public enableVersioning(options: VersioningOptions): void { if (Array.isArray(options.defaultVersion)) { // Drop duplicated versions diff --git a/packages/core/constants.ts b/packages/core/constants.ts index 6b0f56267c2..097176efbf5 100644 --- a/packages/core/constants.ts +++ b/packages/core/constants.ts @@ -1,4 +1,4 @@ -import { EnhancerSubtype } from '@nestjs/common/constants'; +import type { EnhancerSubtype } from '@nestjs/common/internal'; export const MESSAGES = { APPLICATION_START: `Starting Nest application...`, diff --git a/packages/core/discovery/discoverable-meta-host-collection.ts b/packages/core/discovery/discoverable-meta-host-collection.ts index d59a7ad2313..b1fae80dac3 100644 --- a/packages/core/discovery/discoverable-meta-host-collection.ts +++ b/packages/core/discovery/discoverable-meta-host-collection.ts @@ -1,6 +1,6 @@ -import { Type } from '@nestjs/common'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { ModulesContainer } from '../injector/modules-container'; +import type { Type } from '@nestjs/common'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { ModulesContainer } from '../injector/modules-container.js'; export class DiscoverableMetaHostCollection { /** diff --git a/packages/core/discovery/discovery-module.ts b/packages/core/discovery/discovery-module.ts index 46df107fc22..fb23855e8ba 100644 --- a/packages/core/discovery/discovery-module.ts +++ b/packages/core/discovery/discovery-module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { MetadataScanner } from '../metadata-scanner'; -import { DiscoveryService } from './discovery-service'; +import { MetadataScanner } from '../metadata-scanner.js'; +import { DiscoveryService } from './discovery-service.js'; /** * @publicApi diff --git a/packages/core/discovery/discovery-service.ts b/packages/core/discovery/discovery-service.ts index 25c6aacb247..2e23942dfa1 100644 --- a/packages/core/discovery/discovery-service.ts +++ b/packages/core/discovery/discovery-service.ts @@ -1,14 +1,14 @@ import { - CustomDecorator, + type CustomDecorator, flatten, Injectable, SetMetadata, } from '@nestjs/common'; import { uid } from 'uid'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; -import { ModulesContainer } from '../injector/modules-container'; -import { DiscoverableMetaHostCollection } from './discoverable-meta-host-collection'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { Module } from '../injector/module.js'; +import { ModulesContainer } from '../injector/modules-container.js'; +import { DiscoverableMetaHostCollection } from './discoverable-meta-host-collection.js'; /** * @publicApi diff --git a/packages/core/discovery/index.ts b/packages/core/discovery/index.ts index 0e4a7505255..67c7d3a5a66 100644 --- a/packages/core/discovery/index.ts +++ b/packages/core/discovery/index.ts @@ -1,2 +1,2 @@ -export * from './discovery-module'; -export * from './discovery-service'; +export * from './discovery-module.js'; +export * from './discovery-service.js'; diff --git a/packages/core/errors/exception-handler.ts b/packages/core/errors/exception-handler.ts index e34358bf2d0..46dd0943fd9 100644 --- a/packages/core/errors/exception-handler.ts +++ b/packages/core/errors/exception-handler.ts @@ -1,4 +1,4 @@ -import { Logger } from '@nestjs/common/services/logger.service'; +import { Logger } from '@nestjs/common'; export class ExceptionHandler { private static readonly logger = new Logger(ExceptionHandler.name); diff --git a/packages/core/errors/exceptions-zone.ts b/packages/core/errors/exceptions-zone.ts index 375e62e3663..1c1d7c47685 100644 --- a/packages/core/errors/exceptions-zone.ts +++ b/packages/core/errors/exceptions-zone.ts @@ -1,5 +1,5 @@ import { Logger } from '@nestjs/common'; -import { ExceptionHandler } from './exception-handler'; +import { ExceptionHandler } from './exception-handler.js'; const DEFAULT_TEARDOWN = () => process.exit(1); diff --git a/packages/core/errors/exceptions/circular-dependency.exception.ts b/packages/core/errors/exceptions/circular-dependency.exception.ts index 2ca8726ab9d..447e62f74ba 100644 --- a/packages/core/errors/exceptions/circular-dependency.exception.ts +++ b/packages/core/errors/exceptions/circular-dependency.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from './runtime.exception'; +import { RuntimeException } from './runtime.exception.js'; export class CircularDependencyException extends RuntimeException { constructor(context?: string) { diff --git a/packages/core/errors/exceptions/index.ts b/packages/core/errors/exceptions/index.ts index 530968b2f59..6d270cf88d3 100644 --- a/packages/core/errors/exceptions/index.ts +++ b/packages/core/errors/exceptions/index.ts @@ -1,8 +1,8 @@ -export * from './circular-dependency.exception'; -export * from './runtime.exception'; -export * from './unknown-element.exception'; -export * from './invalid-class-scope.exception'; -export * from './invalid-class.exception'; -export * from './unknown-export.exception'; -export * from './unknown-module.exception'; -export * from './undefined-forwardref.exception'; +export * from './circular-dependency.exception.js'; +export * from './runtime.exception.js'; +export * from './unknown-element.exception.js'; +export * from './invalid-class-scope.exception.js'; +export * from './invalid-class.exception.js'; +export * from './unknown-export.exception.js'; +export * from './unknown-module.exception.js'; +export * from './undefined-forwardref.exception.js'; diff --git a/packages/core/errors/exceptions/invalid-class-module.exception.ts b/packages/core/errors/exceptions/invalid-class-module.exception.ts index 0761ea5cec0..1a39f011738 100644 --- a/packages/core/errors/exceptions/invalid-class-module.exception.ts +++ b/packages/core/errors/exceptions/invalid-class-module.exception.ts @@ -1,5 +1,5 @@ -import { USING_INVALID_CLASS_AS_A_MODULE_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { USING_INVALID_CLASS_AS_A_MODULE_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; export class InvalidClassModuleException extends RuntimeException { constructor(metatypeUsedAsAModule: any, scope: any[]) { diff --git a/packages/core/errors/exceptions/invalid-class-scope.exception.ts b/packages/core/errors/exceptions/invalid-class-scope.exception.ts index 252477052f6..b3e8bcd59d5 100644 --- a/packages/core/errors/exceptions/invalid-class-scope.exception.ts +++ b/packages/core/errors/exceptions/invalid-class-scope.exception.ts @@ -1,7 +1,7 @@ -import { Abstract, Type } from '@nestjs/common/interfaces'; -import { isFunction } from '@nestjs/common/utils/shared.utils'; -import { INVALID_CLASS_SCOPE_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { INVALID_CLASS_SCOPE_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; +import type { Abstract, Type } from '@nestjs/common'; +import { isFunction } from '@nestjs/common/internal'; export class InvalidClassScopeException extends RuntimeException { constructor(metatypeOrToken: Type | Abstract | string | symbol) { diff --git a/packages/core/errors/exceptions/invalid-class.exception.ts b/packages/core/errors/exceptions/invalid-class.exception.ts index 48a601ba45d..c872043ec8d 100644 --- a/packages/core/errors/exceptions/invalid-class.exception.ts +++ b/packages/core/errors/exceptions/invalid-class.exception.ts @@ -1,5 +1,5 @@ -import { INVALID_CLASS_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { INVALID_CLASS_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; export class InvalidClassException extends RuntimeException { constructor(value: any) { diff --git a/packages/core/errors/exceptions/invalid-exception-filter.exception.ts b/packages/core/errors/exceptions/invalid-exception-filter.exception.ts index 5ea36ff63fc..73f8a3c94ee 100644 --- a/packages/core/errors/exceptions/invalid-exception-filter.exception.ts +++ b/packages/core/errors/exceptions/invalid-exception-filter.exception.ts @@ -1,5 +1,5 @@ -import { RuntimeException } from './runtime.exception'; -import { INVALID_EXCEPTION_FILTER } from '../messages'; +import { RuntimeException } from './runtime.exception.js'; +import { INVALID_EXCEPTION_FILTER } from '../messages.js'; export class InvalidExceptionFilterException extends RuntimeException { constructor() { diff --git a/packages/core/errors/exceptions/invalid-middleware-configuration.exception.ts b/packages/core/errors/exceptions/invalid-middleware-configuration.exception.ts index 91309b2acc1..ad589ebdb07 100644 --- a/packages/core/errors/exceptions/invalid-middleware-configuration.exception.ts +++ b/packages/core/errors/exceptions/invalid-middleware-configuration.exception.ts @@ -1,5 +1,5 @@ -import { RuntimeException } from './runtime.exception'; -import { INVALID_MIDDLEWARE_CONFIGURATION } from '../messages'; +import { RuntimeException } from './runtime.exception.js'; +import { INVALID_MIDDLEWARE_CONFIGURATION } from '../messages.js'; export class InvalidMiddlewareConfigurationException extends RuntimeException { constructor() { diff --git a/packages/core/errors/exceptions/invalid-middleware.exception.ts b/packages/core/errors/exceptions/invalid-middleware.exception.ts index 8656c9a838e..5032962e029 100644 --- a/packages/core/errors/exceptions/invalid-middleware.exception.ts +++ b/packages/core/errors/exceptions/invalid-middleware.exception.ts @@ -1,5 +1,5 @@ -import { INVALID_MIDDLEWARE_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { INVALID_MIDDLEWARE_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; export class InvalidMiddlewareException extends RuntimeException { constructor(name: string) { diff --git a/packages/core/errors/exceptions/invalid-module.exception.ts b/packages/core/errors/exceptions/invalid-module.exception.ts index fb58000a67a..221e8948e95 100644 --- a/packages/core/errors/exceptions/invalid-module.exception.ts +++ b/packages/core/errors/exceptions/invalid-module.exception.ts @@ -1,5 +1,5 @@ -import { INVALID_MODULE_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { INVALID_MODULE_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; export class InvalidModuleException extends RuntimeException { constructor(parentModule: any, index: number, scope: any[]) { diff --git a/packages/core/errors/exceptions/undefined-dependency.exception.ts b/packages/core/errors/exceptions/undefined-dependency.exception.ts index 62d7f61d756..8761d09f3eb 100644 --- a/packages/core/errors/exceptions/undefined-dependency.exception.ts +++ b/packages/core/errors/exceptions/undefined-dependency.exception.ts @@ -1,7 +1,7 @@ -import { InjectorDependencyContext } from '../../injector/injector'; -import { Module } from '../../injector/module'; -import { UNKNOWN_DEPENDENCIES_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { InjectorDependencyContext } from '../../injector/injector.js'; +import { Module } from '../../injector/module.js'; +import { UNKNOWN_DEPENDENCIES_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; export class UndefinedDependencyException extends RuntimeException { constructor( diff --git a/packages/core/errors/exceptions/undefined-forwardref.exception.ts b/packages/core/errors/exceptions/undefined-forwardref.exception.ts index 5985dcfe0e9..b1cca5775f9 100644 --- a/packages/core/errors/exceptions/undefined-forwardref.exception.ts +++ b/packages/core/errors/exceptions/undefined-forwardref.exception.ts @@ -1,6 +1,6 @@ -import { UNDEFINED_FORWARDREF_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; -import { Type } from '@nestjs/common'; +import { UNDEFINED_FORWARDREF_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; +import type { Type } from '@nestjs/common'; export class UndefinedForwardRefException extends RuntimeException { constructor(scope: Type[]) { diff --git a/packages/core/errors/exceptions/undefined-module.exception.ts b/packages/core/errors/exceptions/undefined-module.exception.ts index c7780ff731e..c1ef490b836 100644 --- a/packages/core/errors/exceptions/undefined-module.exception.ts +++ b/packages/core/errors/exceptions/undefined-module.exception.ts @@ -1,5 +1,5 @@ -import { RuntimeException } from './runtime.exception'; -import { UNDEFINED_MODULE_MESSAGE } from '../messages'; +import { RuntimeException } from './runtime.exception.js'; +import { UNDEFINED_MODULE_MESSAGE } from '../messages.js'; export class UndefinedModuleException extends RuntimeException { constructor(parentModule: any, index: number, scope: any[]) { diff --git a/packages/core/errors/exceptions/unknown-dependencies.exception.ts b/packages/core/errors/exceptions/unknown-dependencies.exception.ts index 57c7207c839..46b2bcfe60e 100644 --- a/packages/core/errors/exceptions/unknown-dependencies.exception.ts +++ b/packages/core/errors/exceptions/unknown-dependencies.exception.ts @@ -1,7 +1,7 @@ -import { InjectorDependencyContext } from '../../injector/injector'; -import { Module } from '../../injector/module'; -import { UNKNOWN_DEPENDENCIES_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { InjectorDependencyContext } from '../../injector/injector.js'; +import { Module } from '../../injector/module.js'; +import { UNKNOWN_DEPENDENCIES_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; export class UnknownDependenciesException extends RuntimeException { public readonly moduleRef: { id: string } | undefined; diff --git a/packages/core/errors/exceptions/unknown-element.exception.ts b/packages/core/errors/exceptions/unknown-element.exception.ts index ae154308fc6..a15a6656d62 100644 --- a/packages/core/errors/exceptions/unknown-element.exception.ts +++ b/packages/core/errors/exceptions/unknown-element.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from './runtime.exception'; +import { RuntimeException } from './runtime.exception.js'; export class UnknownElementException extends RuntimeException { constructor(name?: string | symbol) { diff --git a/packages/core/errors/exceptions/unknown-export.exception.ts b/packages/core/errors/exceptions/unknown-export.exception.ts index fcc6a71d3ae..ceda1789d19 100644 --- a/packages/core/errors/exceptions/unknown-export.exception.ts +++ b/packages/core/errors/exceptions/unknown-export.exception.ts @@ -1,5 +1,5 @@ -import { UNKNOWN_EXPORT_MESSAGE } from '../messages'; -import { RuntimeException } from './runtime.exception'; +import { UNKNOWN_EXPORT_MESSAGE } from '../messages.js'; +import { RuntimeException } from './runtime.exception.js'; export class UnknownExportException extends RuntimeException { constructor(token: string | symbol, moduleName: string) { diff --git a/packages/core/errors/exceptions/unknown-module.exception.ts b/packages/core/errors/exceptions/unknown-module.exception.ts index 998c484dcf5..a805841ade0 100644 --- a/packages/core/errors/exceptions/unknown-module.exception.ts +++ b/packages/core/errors/exceptions/unknown-module.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from './runtime.exception'; +import { RuntimeException } from './runtime.exception.js'; export class UnknownModuleException extends RuntimeException { constructor(moduleName?: string) { diff --git a/packages/core/errors/exceptions/unknown-request-mapping.exception.ts b/packages/core/errors/exceptions/unknown-request-mapping.exception.ts index 561390bd943..218ca773719 100644 --- a/packages/core/errors/exceptions/unknown-request-mapping.exception.ts +++ b/packages/core/errors/exceptions/unknown-request-mapping.exception.ts @@ -1,6 +1,6 @@ import type { Type } from '@nestjs/common'; -import { RuntimeException } from './runtime.exception'; -import { UNKNOWN_REQUEST_MAPPING } from '../messages'; +import { RuntimeException } from './runtime.exception.js'; +import { UNKNOWN_REQUEST_MAPPING } from '../messages.js'; export class UnknownRequestMappingException extends RuntimeException { constructor(metatype: Type) { diff --git a/packages/core/errors/messages.ts b/packages/core/errors/messages.ts index 7838adbb71f..5273ced9053 100644 --- a/packages/core/errors/messages.ts +++ b/packages/core/errors/messages.ts @@ -1,10 +1,10 @@ import type { DynamicModule, ForwardReference, Type } from '@nestjs/common'; -import { isNil, isSymbol } from '@nestjs/common/utils/shared.utils'; import { InjectorDependency, InjectorDependencyContext, -} from '../injector/injector'; -import { Module } from '../injector/module'; +} from '../injector/injector.js'; +import { Module } from '../injector/module.js'; +import { isNil, isSymbol } from '@nestjs/common/internal'; /** * Returns the name of an instance or `undefined` diff --git a/packages/core/exceptions/base-exception-filter-context.ts b/packages/core/exceptions/base-exception-filter-context.ts index 332cbb736d5..e65b6a75260 100644 --- a/packages/core/exceptions/base-exception-filter-context.ts +++ b/packages/core/exceptions/base-exception-filter-context.ts @@ -1,12 +1,14 @@ -import { FILTER_CATCH_EXCEPTIONS } from '@nestjs/common/constants'; -import { Type } from '@nestjs/common/interfaces'; -import { ExceptionFilter } from '@nestjs/common/interfaces/exceptions/exception-filter.interface'; -import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils'; import { iterate } from 'iterare'; -import { ContextCreator } from '../helpers/context-creator'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; +import { ContextCreator } from '../helpers/context-creator.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { + FILTER_CATCH_EXCEPTIONS, + isEmpty, + isFunction, +} from '@nestjs/common/internal'; +import type { Type, ExceptionFilter } from '@nestjs/common'; export class BaseExceptionFilterContext extends ContextCreator { protected moduleContext: string; diff --git a/packages/core/exceptions/base-exception-filter.ts b/packages/core/exceptions/base-exception-filter.ts index 1966614101b..34783f13466 100644 --- a/packages/core/exceptions/base-exception-filter.ts +++ b/packages/core/exceptions/base-exception-filter.ts @@ -1,18 +1,18 @@ import { - ArgumentsHost, - ExceptionFilter, + type ArgumentsHost, + type ExceptionFilter, HttpException, - HttpServer, + type HttpServer, HttpStatus, Inject, IntrinsicException, Logger, Optional, } from '@nestjs/common'; -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { AbstractHttpAdapter } from '../adapters'; -import { MESSAGES } from '../constants'; -import { HttpAdapterHost } from '../helpers/http-adapter-host'; +import { AbstractHttpAdapter } from '../adapters/index.js'; +import { MESSAGES } from '../constants.js'; +import { HttpAdapterHost } from '../helpers/http-adapter-host.js'; +import { isObject } from '@nestjs/common/internal'; export class BaseExceptionFilter implements ExceptionFilter { private static readonly logger = new Logger('ExceptionsHandler'); @@ -79,10 +79,28 @@ export class BaseExceptionFilter implements ExceptionFilter { } /** - * Checks if the thrown error comes from the "http-errors" library. + * Checks if the thrown error is a FastifyError or comes from the "http-errors" library. * @param err error object */ public isHttpError(err: any): err is { statusCode: number; message: string } { - return err?.statusCode && err?.message; + if (!err || typeof err !== 'object') { + return false; + } + + if ( + err.constructor.name === 'FastifyError' && + typeof err.code === 'string' && + typeof err.statusCode === 'number' + ) { + return true; + } + + // "http-errors" error signature + return ( + typeof err.expose === 'boolean' && + typeof err.statusCode === 'number' && + err.status === err.statusCode && + err instanceof Error + ); } } diff --git a/packages/core/exceptions/exceptions-handler.ts b/packages/core/exceptions/exceptions-handler.ts index 36d1b7f22da..6483bdaae89 100644 --- a/packages/core/exceptions/exceptions-handler.ts +++ b/packages/core/exceptions/exceptions-handler.ts @@ -1,10 +1,12 @@ -import { HttpException } from '@nestjs/common'; -import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/exception-filter-metadata.interface'; -import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface'; -import { selectExceptionFilterMetadata } from '@nestjs/common/utils/select-exception-filter-metadata.util'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { InvalidExceptionFilterException } from '../errors/exceptions/invalid-exception-filter.exception'; -import { BaseExceptionFilter } from './base-exception-filter'; +import type { HttpException } from '@nestjs/common'; +import { InvalidExceptionFilterException } from '../errors/exceptions/invalid-exception-filter.exception.js'; +import { BaseExceptionFilter } from './base-exception-filter.js'; +import { + type ExceptionFilterMetadata, + selectExceptionFilterMetadata, + isEmptyArray, +} from '@nestjs/common/internal'; +import type { ArgumentsHost } from '@nestjs/common'; export class ExceptionsHandler extends BaseExceptionFilter { private filters: ExceptionFilterMetadata[] = []; @@ -27,7 +29,7 @@ export class ExceptionsHandler extends BaseExceptionFilter { exception: T, ctx: ArgumentsHost, ): boolean { - if (isEmpty(this.filters)) { + if (isEmptyArray(this.filters)) { return false; } diff --git a/packages/core/exceptions/external-exception-filter-context.ts b/packages/core/exceptions/external-exception-filter-context.ts index d763dfe7349..b6a16b79d7b 100644 --- a/packages/core/exceptions/external-exception-filter-context.ts +++ b/packages/core/exceptions/external-exception-filter-context.ts @@ -1,15 +1,17 @@ -import { EXCEPTION_FILTERS_METADATA } from '@nestjs/common/constants'; -import { Controller } from '@nestjs/common/interfaces'; -import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; import { iterate } from 'iterare'; -import { ApplicationConfig } from '../application-config'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { RouterProxyCallback } from '../router/router-proxy'; -import { BaseExceptionFilterContext } from './base-exception-filter-context'; -import { ExternalExceptionsHandler } from './external-exceptions-handler'; +import { ApplicationConfig } from '../application-config.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { RouterProxyCallback } from '../router/router-proxy.js'; +import { BaseExceptionFilterContext } from './base-exception-filter-context.js'; +import { ExternalExceptionsHandler } from './external-exceptions-handler.js'; +import { + EXCEPTION_FILTERS_METADATA, + type Controller, + type ExceptionFilterMetadata, + isEmptyArray, +} from '@nestjs/common/internal'; export class ExternalExceptionFilterContext extends BaseExceptionFilterContext { constructor( @@ -36,7 +38,7 @@ export class ExternalExceptionFilterContext extends BaseExceptionFilterContext { contextId, inquirerId, ); - if (isEmpty(filters)) { + if (isEmptyArray(filters)) { return exceptionHandler; } exceptionHandler.setCustomFilters(filters.reverse()); diff --git a/packages/core/exceptions/external-exception-filter.ts b/packages/core/exceptions/external-exception-filter.ts index 7ee10c669cc..a181eba2d86 100644 --- a/packages/core/exceptions/external-exception-filter.ts +++ b/packages/core/exceptions/external-exception-filter.ts @@ -1,4 +1,4 @@ -import { ArgumentsHost, IntrinsicException, Logger } from '@nestjs/common'; +import { type ArgumentsHost, IntrinsicException, Logger } from '@nestjs/common'; export class ExternalExceptionFilter { private static readonly logger = new Logger('ExceptionsHandler'); diff --git a/packages/core/exceptions/external-exceptions-handler.ts b/packages/core/exceptions/external-exceptions-handler.ts index 80cb9dd48a5..eee5b6fbb75 100644 --- a/packages/core/exceptions/external-exceptions-handler.ts +++ b/packages/core/exceptions/external-exceptions-handler.ts @@ -1,9 +1,11 @@ -import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions'; -import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface'; -import { selectExceptionFilterMetadata } from '@nestjs/common/utils/select-exception-filter-metadata.util'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { InvalidExceptionFilterException } from '../errors/exceptions/invalid-exception-filter.exception'; -import { ExternalExceptionFilter } from './external-exception-filter'; +import { InvalidExceptionFilterException } from '../errors/exceptions/invalid-exception-filter.exception.js'; +import { ExternalExceptionFilter } from './external-exception-filter.js'; +import { + type ExceptionFilterMetadata, + selectExceptionFilterMetadata, + isEmptyArray, +} from '@nestjs/common/internal'; +import type { ArgumentsHost } from '@nestjs/common'; export class ExternalExceptionsHandler extends ExternalExceptionFilter { private filters: ExceptionFilterMetadata[] = []; @@ -27,7 +29,7 @@ export class ExternalExceptionsHandler extends ExternalExceptionFilter { exception: T, host: ArgumentsHost, ): Promise | null { - if (isEmpty(this.filters)) { + if (isEmptyArray(this.filters)) { return null; } diff --git a/packages/core/exceptions/index.ts b/packages/core/exceptions/index.ts index cc19dee05ad..bc143058bb1 100644 --- a/packages/core/exceptions/index.ts +++ b/packages/core/exceptions/index.ts @@ -1 +1 @@ -export * from './base-exception-filter'; +export * from './base-exception-filter.js'; diff --git a/packages/core/guards/guards-consumer.ts b/packages/core/guards/guards-consumer.ts index c1960bb9f35..357357301d2 100644 --- a/packages/core/guards/guards-consumer.ts +++ b/packages/core/guards/guards-consumer.ts @@ -1,8 +1,8 @@ -import { CanActivate } from '@nestjs/common'; -import { ContextType, Controller } from '@nestjs/common/interfaces'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; +import type { CanActivate } from '@nestjs/common'; import { lastValueFrom, Observable } from 'rxjs'; -import { ExecutionContextHost } from '../helpers/execution-context-host'; +import { ExecutionContextHost } from '../helpers/execution-context-host.js'; +import type { ContextType } from '@nestjs/common'; +import { type Controller, isEmptyArray } from '@nestjs/common/internal'; export class GuardsConsumer { public async tryActivate( @@ -12,7 +12,7 @@ export class GuardsConsumer { callback: (...args: unknown[]) => unknown, type?: TContext, ): Promise { - if (!guards || isEmpty(guards)) { + if (!guards || isEmptyArray(guards)) { return true; } const context = this.createContext(args, instance, callback); diff --git a/packages/core/guards/guards-context-creator.ts b/packages/core/guards/guards-context-creator.ts index 536a4cc0ea4..e5626cb51b4 100644 --- a/packages/core/guards/guards-context-creator.ts +++ b/packages/core/guards/guards-context-creator.ts @@ -1,13 +1,17 @@ -import { CanActivate } from '@nestjs/common'; -import { GUARDS_METADATA } from '@nestjs/common/constants'; -import { Controller, Type } from '@nestjs/common/interfaces'; -import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils'; +import type { CanActivate } from '@nestjs/common'; import { iterate } from 'iterare'; -import { ApplicationConfig } from '../application-config'; -import { ContextCreator } from '../helpers/context-creator'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; +import { ApplicationConfig } from '../application-config.js'; +import { ContextCreator } from '../helpers/context-creator.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { + GUARDS_METADATA, + type Controller, + isEmpty, + isFunction, +} from '@nestjs/common/internal'; +import type { Type } from '@nestjs/common'; export class GuardsContextCreator extends ContextCreator { private moduleContext: string; diff --git a/packages/core/guards/index.ts b/packages/core/guards/index.ts index 61a8b775a33..ed9baa08ee0 100644 --- a/packages/core/guards/index.ts +++ b/packages/core/guards/index.ts @@ -1,3 +1,3 @@ -export * from './constants'; -export * from './guards-consumer'; -export * from './guards-context-creator'; +export * from './constants.js'; +export * from './guards-consumer.js'; +export * from './guards-context-creator.js'; diff --git a/packages/core/helpers/context-creator.ts b/packages/core/helpers/context-creator.ts index 17f455c2b3a..19bbbf254ef 100644 --- a/packages/core/helpers/context-creator.ts +++ b/packages/core/helpers/context-creator.ts @@ -1,6 +1,6 @@ -import { Controller } from '@nestjs/common/interfaces'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { ContextId, InstanceWrapper } from '../injector/instance-wrapper'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { ContextId, InstanceWrapper } from '../injector/instance-wrapper.js'; +import type { Controller } from '@nestjs/common/internal'; export abstract class ContextCreator { public abstract createConcreteContext( diff --git a/packages/core/helpers/context-id-factory.ts b/packages/core/helpers/context-id-factory.ts index fde7f241061..813aae3b5c4 100644 --- a/packages/core/helpers/context-id-factory.ts +++ b/packages/core/helpers/context-id-factory.ts @@ -1,6 +1,6 @@ -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { ContextId, HostComponentInfo } from '../injector/instance-wrapper'; -import { REQUEST_CONTEXT_ID } from '../router/request/request-constants'; +import { ContextId, HostComponentInfo } from '../injector/instance-wrapper.js'; +import { REQUEST_CONTEXT_ID } from '../router/request/request-constants.js'; +import { isObject } from '@nestjs/common/internal'; export function createContextId(): ContextId { /** diff --git a/packages/core/helpers/context-utils.ts b/packages/core/helpers/context-utils.ts index 7ff661a5688..a50ad45fa8f 100644 --- a/packages/core/helpers/context-utils.ts +++ b/packages/core/helpers/context-utils.ts @@ -1,16 +1,13 @@ -import { ParamData } from '@nestjs/common'; +import type { ParamData } from '@nestjs/common'; +import { ExecutionContextHost } from './execution-context-host.js'; import { PARAMTYPES_METADATA, RESPONSE_PASSTHROUGH_METADATA, -} from '@nestjs/common/constants'; -import { - ContextType, - Controller, - PipeTransform, - Type, -} from '@nestjs/common/interfaces'; -import { isFunction } from '@nestjs/common/utils/shared.utils'; -import { ExecutionContextHost } from './execution-context-host'; + type Controller, + isFunction, +} from '@nestjs/common/internal'; +import type { ContextType, PipeTransform, Type } from '@nestjs/common'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; export interface ParamProperties { index: number; @@ -18,6 +15,7 @@ export interface ParamProperties { data: ParamData; pipes: PipeTransform[]; extractValue: IExtractor; + schema?: StandardSchemaV1; } export class ContextUtils { diff --git a/packages/core/helpers/execution-context-host.ts b/packages/core/helpers/execution-context-host.ts index f8ac796b67c..a0fb314d1f0 100644 --- a/packages/core/helpers/execution-context-host.ts +++ b/packages/core/helpers/execution-context-host.ts @@ -1,11 +1,10 @@ -import { ExecutionContext } from '@nestjs/common'; -import { Type } from '@nestjs/common/interfaces'; -import { - ContextType, +import type { ExecutionContext } from '@nestjs/common'; +import type { Type, ContextType } from '@nestjs/common'; +import type { HttpArgumentsHost, RpcArgumentsHost, WsArgumentsHost, -} from '@nestjs/common/interfaces/features/arguments-host.interface'; +} from '@nestjs/common/internal'; export class ExecutionContextHost implements ExecutionContext { private contextType = 'http'; diff --git a/packages/core/helpers/external-context-creator.ts b/packages/core/helpers/external-context-creator.ts index bacc125ba60..9d40d0c13c6 100644 --- a/packages/core/helpers/external-context-creator.ts +++ b/packages/core/helpers/external-context-creator.ts @@ -1,29 +1,32 @@ -import { ForbiddenException, ParamData } from '@nestjs/common'; -import { CUSTOM_ROUTE_ARGS_METADATA } from '@nestjs/common/constants'; +import type { ContextType, PipeTransform } from '@nestjs/common'; import { - ContextType, - Controller, - PipeTransform, -} from '@nestjs/common/interfaces'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; + ArgumentMetadata, + ForbiddenException, + type ParamData, +} from '@nestjs/common'; +import { + CUSTOM_ROUTE_ARGS_METADATA, + type Controller, + isEmptyArray, +} from '@nestjs/common/internal'; import { isObservable, lastValueFrom } from 'rxjs'; -import { ExternalExceptionFilterContext } from '../exceptions/external-exception-filter-context'; -import { GuardsConsumer, GuardsContextCreator } from '../guards'; -import { FORBIDDEN_MESSAGE } from '../guards/constants'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { ContextId } from '../injector/instance-wrapper'; -import { ModulesContainer } from '../injector/modules-container'; +import { ExternalExceptionFilterContext } from '../exceptions/external-exception-filter-context.js'; +import { FORBIDDEN_MESSAGE } from '../guards/constants.js'; +import { GuardsConsumer, GuardsContextCreator } from '../guards/index.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { ContextId } from '../injector/instance-wrapper.js'; +import { ModulesContainer } from '../injector/modules-container.js'; import { InterceptorsConsumer, InterceptorsContextCreator, -} from '../interceptors'; -import { PipesConsumer, PipesContextCreator } from '../pipes'; -import { ContextUtils, ParamProperties } from './context-utils'; -import { ExternalErrorProxy } from './external-proxy'; -import { HandlerMetadataStorage } from './handler-metadata-storage'; -import { ExternalHandlerMetadata } from './interfaces/external-handler-metadata.interface'; -import { ParamsMetadata } from './interfaces/params-metadata.interface'; +} from '../interceptors/index.js'; +import { PipesConsumer, PipesContextCreator } from '../pipes/index.js'; +import { ContextUtils, ParamProperties } from './context-utils.js'; +import { ExternalErrorProxy } from './external-proxy.js'; +import { HandlerMetadataStorage } from './handler-metadata-storage.js'; +import { ExternalHandlerMetadata } from './interfaces/external-handler-metadata.interface.js'; +import { ParamsMetadata } from './interfaces/params-metadata.interface.js'; export interface ParamsFactory { exchangeKeyForValue(type: number, data: ParamData, args: any): any; @@ -264,7 +267,7 @@ export class ExternalContextCreator { this.pipesContextCreator.setModuleContext(moduleContext); return keys.map(key => { - const { index, data, pipes: pipesCollection } = metadata[key]; + const { index, data, pipes: pipesCollection, schema } = metadata[key]; const pipes = this.pipesContextCreator.createConcreteContext( pipesCollection, contextId, @@ -279,13 +282,20 @@ export class ExternalContextCreator { data, contextFactory, ); - return { index, extractValue: customExtractValue, type, data, pipes }; + return { + index, + extractValue: customExtractValue, + type, + data, + pipes, + schema, + }; } const numericType = Number(type); const extractValue = (...args: unknown[]) => paramsFactory.exchangeKeyForValue(numericType, data, args); - return { index, extractValue, type: numericType, data, pipes }; + return { index, extractValue, type: numericType, data, pipes, schema }; }); } @@ -304,12 +314,13 @@ export class ExternalContextCreator { data, metatype, pipes: paramPipes, + schema, } = param; const value = extractValue(...params); args[index] = await this.getParamValue( value, - { metatype, type, data }, + { metatype, type, data, schema } as ArgumentMetadata, pipes.concat(paramPipes), ); }; @@ -320,12 +331,12 @@ export class ExternalContextCreator { public async getParamValue( value: T, - { metatype, type, data }: { metatype: any; type: any; data: any }, + metadata: ArgumentMetadata, pipes: PipeTransform[], ): Promise { - return isEmpty(pipes) + return isEmptyArray(pipes) ? value - : this.pipesConsumer.apply(value, { metatype, type, data }, pipes); + : this.pipesConsumer.apply(value, metadata, pipes); } public async transformToResult(resultOrDeferred: any) { diff --git a/packages/core/helpers/external-proxy.ts b/packages/core/helpers/external-proxy.ts index 98f1cb77767..29f22e45b8d 100644 --- a/packages/core/helpers/external-proxy.ts +++ b/packages/core/helpers/external-proxy.ts @@ -1,6 +1,6 @@ -import { ContextType } from '@nestjs/common/interfaces'; -import { ExternalExceptionsHandler } from '../exceptions/external-exceptions-handler'; -import { ExecutionContextHost } from '../helpers/execution-context-host'; +import { ExternalExceptionsHandler } from '../exceptions/external-exceptions-handler.js'; +import { ExecutionContextHost } from '../helpers/execution-context-host.js'; +import type { ContextType } from '@nestjs/common'; export class ExternalErrorProxy { public createProxy( diff --git a/packages/core/helpers/get-class-scope.ts b/packages/core/helpers/get-class-scope.ts index 58378254a97..6e114875ce5 100644 --- a/packages/core/helpers/get-class-scope.ts +++ b/packages/core/helpers/get-class-scope.ts @@ -1,6 +1,6 @@ -import { Scope } from '@nestjs/common'; -import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants'; -import { Type } from '@nestjs/common/interfaces/type.interface'; +import type { Scope } from '@nestjs/common'; +import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/internal'; +import type { Type } from '@nestjs/common'; export function getClassScope(provider: Type): Scope { const metadata = Reflect.getMetadata(SCOPE_OPTIONS_METADATA, provider); diff --git a/packages/core/helpers/handler-metadata-storage.ts b/packages/core/helpers/handler-metadata-storage.ts index 36e280f2eeb..b60c5a40f95 100644 --- a/packages/core/helpers/handler-metadata-storage.ts +++ b/packages/core/helpers/handler-metadata-storage.ts @@ -1,10 +1,11 @@ -import { Controller, Type } from '@nestjs/common/interfaces'; import { IncomingMessage } from 'http'; import { Observable } from 'rxjs'; -import { CONTROLLER_ID_KEY } from '../injector/constants'; -import { ContextId } from '../injector/instance-wrapper'; -import { HeaderStream } from '../router/sse-stream'; -import { ParamProperties } from './context-utils'; +import { CONTROLLER_ID_KEY } from '../injector/constants.js'; +import { ContextId } from '../injector/instance-wrapper.js'; +import { HeaderStream } from '../router/sse-stream.js'; +import { ParamProperties } from './context-utils.js'; +import type { Type } from '@nestjs/common'; +import type { Controller } from '@nestjs/common/internal'; export const HANDLER_METADATA_SYMBOL = Symbol.for('handler_metadata:cache'); diff --git a/packages/core/helpers/http-adapter-host.ts b/packages/core/helpers/http-adapter-host.ts index 4a17b463b7c..d6a3c3b6d89 100644 --- a/packages/core/helpers/http-adapter-host.ts +++ b/packages/core/helpers/http-adapter-host.ts @@ -1,5 +1,5 @@ import { Observable, ReplaySubject, Subject } from 'rxjs'; -import { AbstractHttpAdapter } from '../adapters/http-adapter'; +import { AbstractHttpAdapter } from '../adapters/http-adapter.js'; /** * Defines the `HttpAdapterHost` object. diff --git a/packages/core/helpers/index.ts b/packages/core/helpers/index.ts index 76f85460602..7069833f810 100644 --- a/packages/core/helpers/index.ts +++ b/packages/core/helpers/index.ts @@ -1,3 +1,3 @@ -export * from './context-id-factory'; -export * from './external-context-creator'; -export * from './http-adapter-host'; +export * from './context-id-factory.js'; +export * from './external-context-creator.js'; +export * from './http-adapter-host.js'; diff --git a/packages/core/helpers/interfaces/external-handler-metadata.interface.ts b/packages/core/helpers/interfaces/external-handler-metadata.interface.ts index ecf99f8f594..ebed6eb29c2 100644 --- a/packages/core/helpers/interfaces/external-handler-metadata.interface.ts +++ b/packages/core/helpers/interfaces/external-handler-metadata.interface.ts @@ -1,5 +1,5 @@ -import { ContextId } from '../../injector'; -import { ParamProperties } from '../context-utils'; +import { ContextId } from '../../injector/index.js'; +import { ParamProperties } from '../context-utils.js'; type ParamPropertiesWithMetatype = ParamProperties & { metatype?: T }; export interface ExternalHandlerMetadata { diff --git a/packages/core/helpers/interfaces/index.ts b/packages/core/helpers/interfaces/index.ts index 85e62522683..f8c92bd1688 100644 --- a/packages/core/helpers/interfaces/index.ts +++ b/packages/core/helpers/interfaces/index.ts @@ -1,2 +1,2 @@ -export * from './external-handler-metadata.interface'; -export * from './params-metadata.interface'; +export * from './external-handler-metadata.interface.js'; +export * from './params-metadata.interface.js'; diff --git a/packages/core/helpers/interfaces/params-metadata.interface.ts b/packages/core/helpers/interfaces/params-metadata.interface.ts index dd1e9c3f9fa..2eac07c2082 100644 --- a/packages/core/helpers/interfaces/params-metadata.interface.ts +++ b/packages/core/helpers/interfaces/params-metadata.interface.ts @@ -1,4 +1,4 @@ -import { ParamData } from '@nestjs/common'; +import type { ParamData } from '@nestjs/common'; export type ParamsMetadata = Record; export interface ParamMetadata { diff --git a/packages/core/helpers/is-durable.ts b/packages/core/helpers/is-durable.ts index 377c84f180d..80b624f6632 100644 --- a/packages/core/helpers/is-durable.ts +++ b/packages/core/helpers/is-durable.ts @@ -1,5 +1,5 @@ -import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants'; -import { Type } from '@nestjs/common/interfaces/type.interface'; +import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/internal'; +import type { Type } from '@nestjs/common'; export function isDurable(provider: Type): boolean | undefined { const metadata = Reflect.getMetadata(SCOPE_OPTIONS_METADATA, provider); diff --git a/packages/core/helpers/load-adapter.ts b/packages/core/helpers/load-adapter.ts index 0f5b1922771..609b952b8d2 100644 --- a/packages/core/helpers/load-adapter.ts +++ b/packages/core/helpers/load-adapter.ts @@ -4,17 +4,17 @@ const MISSING_REQUIRED_DEPENDENCY = ( defaultPlatform: string, transport: string, ) => - `No driver (${transport}) has been selected. In order to take advantage of the default driver, please, ensure to install the "${defaultPlatform}" package ($ npm install ${defaultPlatform}).`; + `No driver (${transport}) has been selected. In order to use the default driver, please, install the "${defaultPlatform}" package ($ npm install ${defaultPlatform}).`; const logger = new Logger('PackageLoader'); -export function loadAdapter( +export async function loadAdapter( defaultPlatform: string, transport: string, loaderFn?: Function, ) { try { - return loaderFn ? loaderFn() : require(defaultPlatform); + return loaderFn ? await loaderFn() : await import(defaultPlatform); } catch (e) { logger.error(MISSING_REQUIRED_DEPENDENCY(defaultPlatform, transport)); process.exit(1); diff --git a/packages/core/helpers/messages.ts b/packages/core/helpers/messages.ts index 3c2fffd7c9c..abd0aaf6126 100644 --- a/packages/core/helpers/messages.ts +++ b/packages/core/helpers/messages.ts @@ -1,8 +1,5 @@ -import { RequestMethod } from '@nestjs/common/enums/request-method.enum'; -import { - VersionValue, - VERSION_NEUTRAL, -} from '@nestjs/common/interfaces/version-options.interface'; +import { RequestMethod, VERSION_NEUTRAL } from '@nestjs/common'; +import type { VersionValue } from '@nestjs/common/internal'; export const MODULE_INIT_MESSAGE = ( text: TemplateStringsArray, diff --git a/packages/core/helpers/optional-require.ts b/packages/core/helpers/optional-require.ts index bbe9a24b8dd..00614318356 100644 --- a/packages/core/helpers/optional-require.ts +++ b/packages/core/helpers/optional-require.ts @@ -1,6 +1,9 @@ -export function optionalRequire(packageName: string, loaderFn?: Function) { +export async function optionalRequire( + packageName: string, + loaderFn?: Function, +) { try { - return loaderFn ? loaderFn() : require(packageName); + return loaderFn ? await loaderFn() : await import(packageName); } catch (e) { return {}; } diff --git a/packages/core/helpers/router-method-factory.ts b/packages/core/helpers/router-method-factory.ts index 944240c8eec..3d7b0e5f914 100644 --- a/packages/core/helpers/router-method-factory.ts +++ b/packages/core/helpers/router-method-factory.ts @@ -1,5 +1,5 @@ -import { HttpServer } from '@nestjs/common'; -import { RequestMethod } from '@nestjs/common/enums/request-method.enum'; +import type { HttpServer } from '@nestjs/common'; +import { RequestMethod } from '@nestjs/common'; export const REQUEST_METHOD_MAP = { [RequestMethod.GET]: 'get', diff --git a/packages/core/hooks/before-app-shutdown.hook.ts b/packages/core/hooks/before-app-shutdown.hook.ts index 42fbed515ab..268398fc39e 100644 --- a/packages/core/hooks/before-app-shutdown.hook.ts +++ b/packages/core/hooks/before-app-shutdown.hook.ts @@ -1,12 +1,9 @@ -import { BeforeApplicationShutdown } from '@nestjs/common'; -import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; +import type { BeforeApplicationShutdown } from '@nestjs/common'; +import { isFunction, isNil } from '@nestjs/common/internal'; import { iterate } from 'iterare'; -import { - getNonTransientInstances, - getTransientInstances, -} from '../injector/helpers/transient-instances'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; +import { Module } from '../injector/module.js'; +import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js'; +import { getSortedHierarchyLevels } from './utils/get-sorted-hierarchy-levels.js'; /** * Checks if the given instance has the `beforeApplicationShutdown` function @@ -24,10 +21,7 @@ function hasBeforeApplicationShutdownHook( /** * Calls the given instances */ -function callOperator( - instances: InstanceWrapper[], - signal?: string, -): Promise[] { +function callOperator(instances: unknown[], signal?: string): Promise[] { return iterate(instances) .filter(instance => !isNil(instance)) .filter(hasBeforeApplicationShutdownHook) @@ -43,27 +37,26 @@ function callOperator( * Calls the `beforeApplicationShutdown` function on the module and its children * (providers / controllers). * - * @param module The module which will be initialized + * @param moduleRef The module which will be initialized * @param signal The signal which caused the shutdown */ export async function callBeforeAppShutdownHook( - module: Module, + moduleRef: Module, signal?: string, ): Promise { - const providers = module.getNonAliasProviders(); + const providers = moduleRef.getNonAliasProviders(); const [_, moduleClassHost] = providers.shift()!; - const instances = [ - ...module.controllers, - ...providers, - ...module.injectables, - ...module.middlewares, - ]; - - const nonTransientInstances = getNonTransientInstances(instances); - await Promise.all(callOperator(nonTransientInstances, signal)); - const transientInstances = getTransientInstances(instances); - await Promise.all(callOperator(transientInstances, signal)); + const groupedInstances = getInstancesGroupedByHierarchyLevel( + moduleRef.controllers, + moduleRef.injectables, + moduleRef.middlewares, + providers, + ); + const levels = getSortedHierarchyLevels(groupedInstances, 'DESC'); + for (const level of levels) { + await Promise.all(callOperator(groupedInstances.get(level)!, signal)); + } const moduleClassInstance = moduleClassHost.instance; if ( moduleClassInstance && diff --git a/packages/core/hooks/index.ts b/packages/core/hooks/index.ts index 0e7a3ac0833..bf5f01a561f 100644 --- a/packages/core/hooks/index.ts +++ b/packages/core/hooks/index.ts @@ -1,5 +1,5 @@ -export * from './on-app-bootstrap.hook'; -export * from './on-app-shutdown.hook'; -export * from './on-module-destroy.hook'; -export * from './on-module-init.hook'; -export * from './before-app-shutdown.hook'; +export * from './on-app-bootstrap.hook.js'; +export * from './on-app-shutdown.hook.js'; +export * from './on-module-destroy.hook.js'; +export * from './on-module-init.hook.js'; +export * from './before-app-shutdown.hook.js'; diff --git a/packages/core/hooks/on-app-bootstrap.hook.ts b/packages/core/hooks/on-app-bootstrap.hook.ts index 9ec8ca6ebdf..9b2c1c40c18 100644 --- a/packages/core/hooks/on-app-bootstrap.hook.ts +++ b/packages/core/hooks/on-app-bootstrap.hook.ts @@ -1,12 +1,9 @@ -import { OnApplicationBootstrap } from '@nestjs/common'; -import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; +import type { OnApplicationBootstrap } from '@nestjs/common'; +import { isFunction, isNil } from '@nestjs/common/internal'; import { iterate } from 'iterare'; -import { - getNonTransientInstances, - getTransientInstances, -} from '../injector/helpers/transient-instances'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; +import { Module } from '../injector/module.js'; +import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js'; +import { getSortedHierarchyLevels } from './utils/get-sorted-hierarchy-levels.js'; /** * Checks if the given instance has the `onApplicationBootstrap` function @@ -24,7 +21,7 @@ function hasOnAppBootstrapHook( /** * Calls the given instances */ -function callOperator(instances: InstanceWrapper[]): Promise[] { +function callOperator(instances: unknown[]): Promise[] { return iterate(instances) .filter(instance => !isNil(instance)) .filter(hasOnAppBootstrapHook) @@ -38,24 +35,24 @@ function callOperator(instances: InstanceWrapper[]): Promise[] { * Calls the `onApplicationBootstrap` function on the module and its children * (providers / controllers). * - * @param module The module which will be initialized + * @param moduleRef The module which will be initialized */ -export async function callModuleBootstrapHook(module: Module): Promise { - const providers = module.getNonAliasProviders(); +export async function callModuleBootstrapHook(moduleRef: Module): Promise { + const providers = moduleRef.getNonAliasProviders(); // Module (class) instance is the first element of the providers array // Lifecycle hook has to be called once all classes are properly initialized const [_, moduleClassHost] = providers.shift()!; - const instances = [ - ...module.controllers, - ...providers, - ...module.injectables, - ...module.middlewares, - ]; + const groupedInstances = getInstancesGroupedByHierarchyLevel( + moduleRef.controllers, + moduleRef.injectables, + moduleRef.middlewares, + providers, + ); - const nonTransientInstances = getNonTransientInstances(instances); - await Promise.all(callOperator(nonTransientInstances)); - const transientInstances = getTransientInstances(instances); - await Promise.all(callOperator(transientInstances)); + const levels = getSortedHierarchyLevels(groupedInstances); + for (const level of levels) { + await Promise.all(callOperator(groupedInstances.get(level)!)); + } // Call the instance itself const moduleClassInstance = moduleClassHost.instance; diff --git a/packages/core/hooks/on-app-shutdown.hook.ts b/packages/core/hooks/on-app-shutdown.hook.ts index 3c9b2b900ed..4fbd5957260 100644 --- a/packages/core/hooks/on-app-shutdown.hook.ts +++ b/packages/core/hooks/on-app-shutdown.hook.ts @@ -1,12 +1,9 @@ -import { OnApplicationShutdown } from '@nestjs/common'; -import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; +import type { OnApplicationShutdown } from '@nestjs/common'; +import { isFunction, isNil } from '@nestjs/common/internal'; import { iterate } from 'iterare'; -import { - getNonTransientInstances, - getTransientInstances, -} from '../injector/helpers/transient-instances'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; +import { Module } from '../injector/module.js'; +import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js'; +import { getSortedHierarchyLevels } from './utils/get-sorted-hierarchy-levels.js'; /** * Checks if the given instance has the `onApplicationShutdown` function @@ -22,10 +19,7 @@ function hasOnAppShutdownHook( /** * Calls the given instances */ -function callOperator( - instances: InstanceWrapper[], - signal?: string, -): Promise[] { +function callOperator(instances: unknown[], signal?: string): Promise[] { return iterate(instances) .filter(instance => !isNil(instance)) .filter(hasOnAppShutdownHook) @@ -39,29 +33,28 @@ function callOperator( * Calls the `onApplicationShutdown` function on the module and its children * (providers / controllers). * - * @param module The module which will be initialized + * @param moduleRef The module which will be initialized * @param signal */ export async function callAppShutdownHook( - module: Module, + moduleRef: Module, signal?: string, ): Promise { - const providers = module.getNonAliasProviders(); + const providers = moduleRef.getNonAliasProviders(); // Module (class) instance is the first element of the providers array // Lifecycle hook has to be called once all classes are properly initialized const [_, moduleClassHost] = providers.shift()!; - const instances = [ - ...module.controllers, - ...providers, - ...module.injectables, - ...module.middlewares, - ]; - - const nonTransientInstances = getNonTransientInstances(instances); - await Promise.all(callOperator(nonTransientInstances, signal)); - const transientInstances = getTransientInstances(instances); - await Promise.all(callOperator(transientInstances, signal)); + const groupedInstances = getInstancesGroupedByHierarchyLevel( + moduleRef.controllers, + moduleRef.injectables, + moduleRef.middlewares, + providers, + ); + const levels = getSortedHierarchyLevels(groupedInstances, 'DESC'); + for (const level of levels) { + await Promise.all(callOperator(groupedInstances.get(level)!, signal)); + } // Call the instance itself const moduleClassInstance = moduleClassHost.instance; if ( diff --git a/packages/core/hooks/on-module-destroy.hook.ts b/packages/core/hooks/on-module-destroy.hook.ts index 8c2692372a5..2a0646e12eb 100644 --- a/packages/core/hooks/on-module-destroy.hook.ts +++ b/packages/core/hooks/on-module-destroy.hook.ts @@ -1,12 +1,9 @@ -import { OnModuleDestroy } from '@nestjs/common'; -import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; +import type { OnModuleDestroy } from '@nestjs/common'; +import { isFunction, isNil } from '@nestjs/common/internal'; import { iterate } from 'iterare'; -import { - getNonTransientInstances, - getTransientInstances, -} from '../injector/helpers/transient-instances'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; +import { Module } from '../injector/module.js'; +import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js'; +import { getSortedHierarchyLevels } from './utils/get-sorted-hierarchy-levels.js'; /** * Returns true or false if the given instance has a `onModuleDestroy` function @@ -22,7 +19,7 @@ function hasOnModuleDestroyHook( /** * Calls the given instances onModuleDestroy hook */ -function callOperator(instances: InstanceWrapper[]): Promise[] { +function callOperator(instances: unknown[]): Promise[] { return iterate(instances) .filter(instance => !isNil(instance)) .filter(hasOnModuleDestroyHook) @@ -36,25 +33,24 @@ function callOperator(instances: InstanceWrapper[]): Promise[] { * Calls the `onModuleDestroy` function on the module and its children * (providers / controllers). * - * @param module The module which will be initialized + * @param moduleRef The module which will be initialized */ -export async function callModuleDestroyHook(module: Module): Promise { - const providers = module.getNonAliasProviders(); +export async function callModuleDestroyHook(moduleRef: Module): Promise { + const providers = moduleRef.getNonAliasProviders(); // Module (class) instance is the first element of the providers array // Lifecycle hook has to be called once all classes are properly destroyed const [_, moduleClassHost] = providers.shift()!; - const instances = [ - ...module.controllers, - ...providers, - ...module.injectables, - ...module.middlewares, - ]; + const groupedInstances = getInstancesGroupedByHierarchyLevel( + moduleRef.controllers, + moduleRef.injectables, + moduleRef.middlewares, + providers, + ); - const nonTransientInstances = getNonTransientInstances(instances); - await Promise.all(callOperator(nonTransientInstances)); - - const transientInstances = getTransientInstances(instances); - await Promise.all(callOperator(transientInstances)); + const levels = getSortedHierarchyLevels(groupedInstances, 'DESC'); + for (const level of levels) { + await Promise.all(callOperator(groupedInstances.get(level)!)); + } // Call the module instance itself const moduleClassInstance = moduleClassHost.instance; diff --git a/packages/core/hooks/on-module-init.hook.ts b/packages/core/hooks/on-module-init.hook.ts index 029eaecadf3..40ca5834d48 100644 --- a/packages/core/hooks/on-module-init.hook.ts +++ b/packages/core/hooks/on-module-init.hook.ts @@ -1,12 +1,9 @@ -import { OnModuleInit } from '@nestjs/common'; -import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; +import type { OnModuleInit } from '@nestjs/common'; +import { isFunction, isNil } from '@nestjs/common/internal'; import { iterate } from 'iterare'; -import { - getNonTransientInstances, - getTransientInstances, -} from '../injector/helpers/transient-instances'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; +import { Module } from '../injector/module.js'; +import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js'; +import { getSortedHierarchyLevels } from './utils/get-sorted-hierarchy-levels.js'; /** * Returns true or false if the given instance has a `onModuleInit` function @@ -20,7 +17,7 @@ function hasOnModuleInitHook(instance: unknown): instance is OnModuleInit { /** * Calls the given instances */ -function callOperator(instances: InstanceWrapper[]): Promise[] { +function callOperator(instances: unknown[]): Promise[] { return iterate(instances) .filter(instance => !isNil(instance)) .filter(hasOnModuleInitHook) @@ -32,25 +29,25 @@ function callOperator(instances: InstanceWrapper[]): Promise[] { * Calls the `onModuleInit` function on the module and its children * (providers / controllers). * - * @param module The module which will be initialized + * @param moduleRef The module which will be initialized */ -export async function callModuleInitHook(module: Module): Promise { - const providers = module.getNonAliasProviders(); +export async function callModuleInitHook(moduleRef: Module): Promise { + const providers = moduleRef.getNonAliasProviders(); // Module (class) instance is the first element of the providers array // Lifecycle hook has to be called once all classes are properly initialized const [_, moduleClassHost] = providers.shift()!; - const instances = [ - ...module.controllers, - ...providers, - ...module.injectables, - ...module.middlewares, - ]; - const nonTransientInstances = getNonTransientInstances(instances); - await Promise.all(callOperator(nonTransientInstances)); + const groupedInstances = getInstancesGroupedByHierarchyLevel( + moduleRef.controllers, + moduleRef.injectables, + moduleRef.middlewares, + providers, + ); - const transientInstances = getTransientInstances(instances); - await Promise.all(callOperator(transientInstances)); + const levels = getSortedHierarchyLevels(groupedInstances); + for (const level of levels) { + await Promise.all(callOperator(groupedInstances.get(level)!)); + } // Call the instance itself const moduleClassInstance = moduleClassHost.instance; diff --git a/packages/core/hooks/utils/get-instances-grouped-by-hierarchy-level.ts b/packages/core/hooks/utils/get-instances-grouped-by-hierarchy-level.ts new file mode 100644 index 00000000000..480557b0923 --- /dev/null +++ b/packages/core/hooks/utils/get-instances-grouped-by-hierarchy-level.ts @@ -0,0 +1,40 @@ +import { InjectionToken } from '@nestjs/common'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; + +export function getInstancesGroupedByHierarchyLevel( + ...collections: Array< + | Map + | Array<[InjectionToken, InstanceWrapper]> + > +): Map { + const groupedByHierarchyLevel = new Map(); + + for (const collection of collections) { + for (const [_, wrapper] of collection) { + if (!wrapper.isDependencyTreeStatic()) { + continue; + } + + const level = wrapper.hierarchyLevel; + if (!groupedByHierarchyLevel.has(level)) { + groupedByHierarchyLevel.set(level, []); + } + + const byHierarchyLevelGroup = groupedByHierarchyLevel.get(level); + if (wrapper.isTransient) { + const staticTransientInstances = wrapper + .getStaticTransientInstances() + .filter(i => !!i) + .map(i => i.instance); + byHierarchyLevelGroup!.push(...staticTransientInstances); + continue; + } + + if (wrapper.instance) { + byHierarchyLevelGroup!.push(wrapper.instance); + } + } + } + + return groupedByHierarchyLevel; +} diff --git a/packages/core/hooks/utils/get-sorted-hierarchy-levels.ts b/packages/core/hooks/utils/get-sorted-hierarchy-levels.ts new file mode 100644 index 00000000000..1a71f972b77 --- /dev/null +++ b/packages/core/hooks/utils/get-sorted-hierarchy-levels.ts @@ -0,0 +1,11 @@ +export function getSortedHierarchyLevels( + groups: Map, + order: 'ASC' | 'DESC' = 'ASC', +): number[] { + const comparator = + order === 'ASC' + ? (a: number, b: number) => a - b + : (a: number, b: number) => b - a; + const levels = Array.from(groups.keys()).sort(comparator); + return levels; +} diff --git a/packages/core/index.ts b/packages/core/index.ts index dd16d083d61..4048b5ff58c 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -6,19 +6,24 @@ */ import 'reflect-metadata'; -export * from './adapters'; -export * from './application-config'; -export { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from './constants'; -export * from './discovery'; -export * from './exceptions'; -export * from './helpers'; -export * from './injector'; -export * from './inspector'; -export * from './metadata-scanner'; -export * from './middleware'; -export * from './nest-application'; -export * from './nest-application-context'; -export { NestFactory } from './nest-factory'; -export * from './repl'; -export * from './router'; -export * from './services'; +export * from './adapters/index.js'; +export * from './application-config.js'; +export { + APP_FILTER, + APP_GUARD, + APP_INTERCEPTOR, + APP_PIPE, +} from './constants.js'; +export * from './discovery/index.js'; +export * from './exceptions/index.js'; +export * from './helpers/index.js'; +export * from './injector/index.js'; +export * from './inspector/index.js'; +export * from './metadata-scanner.js'; +export * from './middleware/index.js'; +export * from './nest-application.js'; +export * from './nest-application-context.js'; +export { NestFactory } from './nest-factory.js'; +export * from './repl/index.js'; +export * from './router/index.js'; +export * from './services/index.js'; diff --git a/packages/core/injector/abstract-instance-resolver.ts b/packages/core/injector/abstract-instance-resolver.ts index 7ea66a4a944..24b22a04cbe 100644 --- a/packages/core/injector/abstract-instance-resolver.ts +++ b/packages/core/injector/abstract-instance-resolver.ts @@ -1,13 +1,13 @@ -import { Abstract, Scope, Type } from '@nestjs/common'; -import { GetOrResolveOptions } from '@nestjs/common/interfaces'; +import { type Abstract, Scope, type Type } from '@nestjs/common'; import { InvalidClassScopeException, UnknownElementException, -} from '../errors/exceptions'; -import { Injector } from './injector'; -import { InstanceLink, InstanceLinksHost } from './instance-links-host'; -import { ContextId } from './instance-wrapper'; -import { Module } from './module'; +} from '../errors/exceptions/index.js'; +import { Injector } from './injector.js'; +import { InstanceLink, InstanceLinksHost } from './instance-links-host.js'; +import { ContextId } from './instance-wrapper.js'; +import { Module } from './module.js'; +import type { GetOrResolveOptions } from '@nestjs/common/internal'; export abstract class AbstractInstanceResolver { protected abstract instanceLinksHost: InstanceLinksHost; diff --git a/packages/core/injector/compiler.ts b/packages/core/injector/compiler.ts index 075d97bc3ae..515a7484875 100644 --- a/packages/core/injector/compiler.ts +++ b/packages/core/injector/compiler.ts @@ -1,9 +1,5 @@ -import { - DynamicModule, - ForwardReference, - Type, -} from '@nestjs/common/interfaces'; -import { ModuleOpaqueKeyFactory } from './opaque-key-factory/interfaces/module-opaque-key-factory.interface'; +import { ModuleOpaqueKeyFactory } from './opaque-key-factory/interfaces/module-opaque-key-factory.interface.js'; +import type { DynamicModule, ForwardReference, Type } from '@nestjs/common'; export interface ModuleFactory { type: Type; diff --git a/packages/core/injector/constants.ts b/packages/core/injector/constants.ts index 3bbcb2687a6..dfe4e527921 100644 --- a/packages/core/injector/constants.ts +++ b/packages/core/injector/constants.ts @@ -1,4 +1,4 @@ -import { ContextId } from './instance-wrapper'; +import { ContextId } from './instance-wrapper.js'; export const CONTROLLER_ID_KEY = 'CONTROLLER_ID'; diff --git a/packages/core/injector/container.ts b/packages/core/injector/container.ts index 555e90a5efd..85a16ddacfb 100644 --- a/packages/core/injector/container.ts +++ b/packages/core/injector/container.ts @@ -1,29 +1,30 @@ -import { DynamicModule, Provider } from '@nestjs/common'; -import { - EnhancerSubtype, - GLOBAL_MODULE_METADATA, -} from '@nestjs/common/constants'; -import { Injectable, Type } from '@nestjs/common/interfaces'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { ApplicationConfig } from '../application-config'; -import { DiscoverableMetaHostCollection } from '../discovery/discoverable-meta-host-collection'; +import type { DynamicModule, Provider } from '@nestjs/common'; +import { ApplicationConfig } from '../application-config.js'; +import { DiscoverableMetaHostCollection } from '../discovery/discoverable-meta-host-collection.js'; import { CircularDependencyException, UndefinedForwardRefException, UnknownModuleException, -} from '../errors/exceptions'; -import { InitializeOnPreviewAllowlist } from '../inspector/initialize-on-preview.allowlist'; -import { SerializedGraph } from '../inspector/serialized-graph'; -import { REQUEST } from '../router/request/request-constants'; -import { ModuleCompiler, ModuleFactory } from './compiler'; -import { ContextId } from './instance-wrapper'; -import { InternalCoreModule } from './internal-core-module/internal-core-module'; -import { InternalProvidersStorage } from './internal-providers-storage'; -import { Module } from './module'; -import { ModulesContainer } from './modules-container'; -import { ByReferenceModuleOpaqueKeyFactory } from './opaque-key-factory/by-reference-module-opaque-key-factory'; -import { DeepHashedModuleOpaqueKeyFactory } from './opaque-key-factory/deep-hashed-module-opaque-key-factory'; -import { ModuleOpaqueKeyFactory } from './opaque-key-factory/interfaces/module-opaque-key-factory.interface'; +} from '../errors/exceptions/index.js'; +import { InitializeOnPreviewAllowlist } from '../inspector/initialize-on-preview.allowlist.js'; +import { SerializedGraph } from '../inspector/serialized-graph.js'; +import { REQUEST } from '../router/request/request-constants.js'; +import { ModuleCompiler, ModuleFactory } from './compiler.js'; +import { ContextId } from './instance-wrapper.js'; +import { InternalCoreModule } from './internal-core-module/internal-core-module.js'; +import { InternalProvidersStorage } from './internal-providers-storage.js'; +import { Module } from './module.js'; +import { ModulesContainer } from './modules-container.js'; +import { ByReferenceModuleOpaqueKeyFactory } from './opaque-key-factory/by-reference-module-opaque-key-factory.js'; +import { DeepHashedModuleOpaqueKeyFactory } from './opaque-key-factory/deep-hashed-module-opaque-key-factory.js'; +import { ModuleOpaqueKeyFactory } from './opaque-key-factory/interfaces/module-opaque-key-factory.interface.js'; +import { + type EnhancerSubtype, + GLOBAL_MODULE_METADATA, + type Injectable, + type NestApplicationContextOptions, +} from '@nestjs/common/internal'; +import type { Type } from '@nestjs/common'; type ModuleMetatype = Type | DynamicModule | Promise; type ModuleScope = Type[]; diff --git a/packages/core/injector/helpers/provider-classifier.ts b/packages/core/injector/helpers/provider-classifier.ts index b883a4ea83b..44070ee96df 100644 --- a/packages/core/injector/helpers/provider-classifier.ts +++ b/packages/core/injector/helpers/provider-classifier.ts @@ -1,10 +1,10 @@ -import { +import type { ClassProvider, FactoryProvider, Provider, ValueProvider, } from '@nestjs/common'; -import { isUndefined } from '@nestjs/common/utils/shared.utils'; +import { isUndefined } from '@nestjs/common/internal'; export function isClassProvider( provider: Provider, diff --git a/packages/core/injector/helpers/transient-instances.ts b/packages/core/injector/helpers/transient-instances.ts deleted file mode 100644 index 4e7f4afd6cb..00000000000 --- a/packages/core/injector/helpers/transient-instances.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { InjectionToken } from '@nestjs/common'; -import { iterate } from 'iterare'; -import { InstanceWrapper } from '../instance-wrapper'; - -/** - * Returns the instances which are transient - * @param instances The instances which should be checked whether they are transient - */ -export function getTransientInstances( - instances: [InjectionToken, InstanceWrapper][], -): InstanceWrapper[] { - return iterate(instances) - .filter(([_, wrapper]) => wrapper.isDependencyTreeStatic()) - .map(([_, wrapper]) => wrapper.getStaticTransientInstances()) - .flatten() - .filter(item => !!item) - .map(({ instance }: any) => instance) - .toArray() as InstanceWrapper[]; -} - -/** - * Returns the instances which are not transient - * @param instances The instances which should be checked whether they are transient - */ -export function getNonTransientInstances( - instances: [InjectionToken, InstanceWrapper][], -): InstanceWrapper[] { - return iterate(instances) - .filter( - ([key, wrapper]) => - wrapper.isDependencyTreeStatic() && !wrapper.isTransient, - ) - .map(([key, { instance }]) => instance) - .toArray() as InstanceWrapper[]; -} diff --git a/packages/core/injector/index.ts b/packages/core/injector/index.ts index 21e406d0661..494235ce12c 100644 --- a/packages/core/injector/index.ts +++ b/packages/core/injector/index.ts @@ -1,6 +1,6 @@ -export * from './container'; -export * from './inquirer'; -export { ContextId, HostComponentInfo } from './instance-wrapper'; -export * from './lazy-module-loader/lazy-module-loader'; -export * from './module-ref'; -export * from './modules-container'; +export * from './container.js'; +export * from './inquirer/index.js'; +export { ContextId, HostComponentInfo } from './instance-wrapper.js'; +export * from './lazy-module-loader/lazy-module-loader.js'; +export * from './module-ref.js'; +export * from './modules-container.js'; diff --git a/packages/core/injector/injector.ts b/packages/core/injector/injector.ts index 85f5d0204ac..c6136938c47 100644 --- a/packages/core/injector/injector.ts +++ b/packages/core/injector/injector.ts @@ -1,48 +1,43 @@ +import type { ForwardReference, Type } from '@nestjs/common'; import { - InjectionToken, + type InjectionToken, Logger, - LoggerService, - OptionalFactoryDependency, + type LoggerService, + type OptionalFactoryDependency, } from '@nestjs/common'; import { + type Controller, + type Injectable, OPTIONAL_DEPS_METADATA, OPTIONAL_PROPERTY_DEPS_METADATA, PARAMTYPES_METADATA, PROPERTY_DEPS_METADATA, SELF_DECLARED_DEPS_METADATA, -} from '@nestjs/common/constants'; -import { - Controller, - ForwardReference, - Injectable, - Type, -} from '@nestjs/common/interfaces'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { + clc, isFunction, isNil, isObject, isString, isSymbol, isUndefined, -} from '@nestjs/common/utils/shared.utils'; +} from '@nestjs/common/internal'; import { iterate } from 'iterare'; import { performance } from 'perf_hooks'; -import { CircularDependencyException } from '../errors/exceptions'; -import { RuntimeException } from '../errors/exceptions/runtime.exception'; -import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception'; -import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception'; -import { Barrier } from '../helpers/barrier'; -import { STATIC_CONTEXT } from './constants'; -import { INQUIRER } from './inquirer'; +import { CircularDependencyException } from '../errors/exceptions/index.js'; +import { RuntimeException } from '../errors/exceptions/runtime.exception.js'; +import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception.js'; +import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception.js'; +import { Barrier } from '../helpers/barrier.js'; +import { STATIC_CONTEXT } from './constants.js'; +import { INQUIRER } from './inquirer/index.js'; import { ContextId, InstancePerContext, InstanceWrapper, PropertyMetadata, -} from './instance-wrapper'; -import { Module } from './module'; -import { SettlementSignal } from './settlement-signal'; +} from './instance-wrapper.js'; +import { Module } from './module.js'; +import { SettlementSignal } from './settlement-signal.js'; /** * The type of an injectable dependency @@ -164,7 +159,7 @@ export class Injector { } try { const t0 = this.getNowTimestamp(); - const callback = async (instances: unknown[]) => { + const callback = async (instances: unknown[], depth = 0) => { const properties = await this.resolveProperties( wrapper, moduleRef, @@ -181,6 +176,8 @@ export class Injector { inquirer, ); this.applyProperties(instance, properties); + + wrapper.hierarchyLevel = depth + 1; wrapper.initTime = this.getNowTimestamp() - t0; settlementSignal.complete(); }; @@ -291,7 +288,7 @@ export class Injector { wrapper: InstanceWrapper, moduleRef: Module, inject: InjectorDependency[] | undefined, - callback: (args: unknown[]) => void | Promise, + callback: (args: unknown[], depth?: number) => void | Promise, contextId = STATIC_CONTEXT, inquirer?: InstanceWrapper, parentInquirer?: InstanceWrapper, @@ -315,6 +312,7 @@ export class Injector { const paramBarrier = new Barrier(dependencies.length); let isResolved = true; + let depth = 0; const resolveParam = async (param: unknown, index: number) => { try { if (this.isInquirer(param, parentInquirer)) { @@ -360,6 +358,11 @@ export class Injector { contextId, effectiveInquirer, ); + + if (paramWrapperWithInstance.hierarchyLevel > depth) { + depth = paramWrapperWithInstance.hierarchyLevel; + } + const instanceHost = paramWrapperWithInstance.getInstanceByContextId( this.getContextId(contextId, paramWrapperWithInstance), this.getInquirerId(effectiveInquirer), @@ -384,7 +387,7 @@ export class Injector { } }; const instances = await Promise.all(dependencies.map(resolveParam)); - isResolved && (await callback(instances)); + isResolved && (await callback(instances, depth)); } public getClassDependencies( @@ -448,7 +451,7 @@ export class Injector { } public reflectOptionalParams(type: Type): any[] { - return Reflect.getMetadata(OPTIONAL_DEPS_METADATA, type) || []; + return Reflect.getOwnMetadata(OPTIONAL_DEPS_METADATA, type) || []; } public reflectSelfParams(type: Type): any[] { @@ -809,7 +812,7 @@ export class Injector { properties: PropertyDependency[], ): void { if (!isObject(instance)) { - return undefined; + return; } iterate(properties) .filter(item => !isNil(item.instance)) diff --git a/packages/core/injector/inquirer/index.ts b/packages/core/injector/inquirer/index.ts index cec511a330f..66995589fe2 100644 --- a/packages/core/injector/inquirer/index.ts +++ b/packages/core/injector/inquirer/index.ts @@ -1 +1 @@ -export * from './inquirer-constants'; +export * from './inquirer-constants.js'; diff --git a/packages/core/injector/inquirer/inquirer-providers.ts b/packages/core/injector/inquirer/inquirer-providers.ts index 20a49d21782..a07011e6615 100644 --- a/packages/core/injector/inquirer/inquirer-providers.ts +++ b/packages/core/injector/inquirer/inquirer-providers.ts @@ -1,5 +1,5 @@ -import { Provider, Scope } from '@nestjs/common'; -import { INQUIRER } from './inquirer-constants'; +import { type Provider, Scope } from '@nestjs/common'; +import { INQUIRER } from './inquirer-constants.js'; const noop = () => {}; export const inquirerProvider: Provider = { diff --git a/packages/core/injector/instance-links-host.ts b/packages/core/injector/instance-links-host.ts index bd6839e30e4..d20b936e89e 100644 --- a/packages/core/injector/instance-links-host.ts +++ b/packages/core/injector/instance-links-host.ts @@ -1,9 +1,9 @@ -import { InjectionToken } from '@nestjs/common'; -import { isFunction } from '@nestjs/common/utils/shared.utils'; -import { UnknownElementException } from '../errors/exceptions/unknown-element.exception'; -import { NestContainer } from './container'; -import { InstanceWrapper } from './instance-wrapper'; -import { Module } from './module'; +import type { InjectionToken } from '@nestjs/common'; +import { UnknownElementException } from '../errors/exceptions/unknown-element.exception.js'; +import { NestContainer } from './container.js'; +import { InstanceWrapper } from './instance-wrapper.js'; +import { Module } from './module.js'; +import { isFunction } from '@nestjs/common/internal'; type HostCollection = 'providers' | 'controllers' | 'injectables'; diff --git a/packages/core/injector/instance-loader.ts b/packages/core/injector/instance-loader.ts index 489ffdef44c..ae2ed7fec08 100644 --- a/packages/core/injector/instance-loader.ts +++ b/packages/core/injector/instance-loader.ts @@ -1,12 +1,11 @@ -import { Logger, LoggerService } from '@nestjs/common'; -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { Injectable } from '@nestjs/common/interfaces/injectable.interface'; -import { MODULE_INIT_MESSAGE } from '../helpers/messages'; -import { GraphInspector } from '../inspector/graph-inspector'; -import { NestContainer } from './container'; -import { Injector } from './injector'; -import { InternalCoreModule } from './internal-core-module/internal-core-module'; -import { Module } from './module'; +import { Logger, type LoggerService } from '@nestjs/common'; +import { MODULE_INIT_MESSAGE } from '../helpers/messages.js'; +import { GraphInspector } from '../inspector/graph-inspector.js'; +import { NestContainer } from './container.js'; +import { Injector } from './injector.js'; +import { InternalCoreModule } from './internal-core-module/internal-core-module.js'; +import { Module } from './module.js'; +import type { Controller, Injectable } from '@nestjs/common/internal'; export class InstanceLoader { constructor( diff --git a/packages/core/injector/instance-wrapper.ts b/packages/core/injector/instance-wrapper.ts index 962e9259b94..a4726ce7c04 100644 --- a/packages/core/injector/instance-wrapper.ts +++ b/packages/core/injector/instance-wrapper.ts @@ -1,23 +1,29 @@ -import { Logger, LoggerService, Provider, Scope, Type } from '@nestjs/common'; -import { EnhancerSubtype } from '@nestjs/common/constants'; -import { FactoryProvider, InjectionToken } from '@nestjs/common/interfaces'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; +import type { FactoryProvider, InjectionToken } from '@nestjs/common'; import { + Logger, + type LoggerService, + type Provider, + Scope, + type Type, +} from '@nestjs/common'; +import { + type EnhancerSubtype, + clc, isNil, isString, isUndefined, -} from '@nestjs/common/utils/shared.utils'; + randomStringGenerator, +} from '@nestjs/common/internal'; import { iterate } from 'iterare'; -import { UuidFactory } from '../inspector/uuid-factory'; -import { STATIC_CONTEXT } from './constants'; +import { UuidFactory } from '../inspector/uuid-factory.js'; +import { STATIC_CONTEXT } from './constants.js'; import { isClassProvider, isFactoryProvider, isValueProvider, -} from './helpers/provider-classifier'; -import { Module } from './module'; -import { SettlementSignal } from './settlement-signal'; +} from './helpers/provider-classifier.js'; +import { Module } from './module.js'; +import { SettlementSignal } from './settlement-signal.js'; export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache'); export const INSTANCE_ID_SYMBOL = Symbol.for('instance_metadata:id'); @@ -83,6 +89,16 @@ export class InstanceWrapper { | undefined; private isTreeStatic: boolean | undefined; private isTreeDurable: boolean | undefined; + private _hierarchyLevel = 0; + + get hierarchyLevel(): number { + return this._hierarchyLevel; + } + + set hierarchyLevel(level: number) { + this._hierarchyLevel = level; + } + /** * The root inquirer reference. Present only if child instance wrapper * is transient and has a parent inquirer. diff --git a/packages/core/injector/internal-core-module/index.ts b/packages/core/injector/internal-core-module/index.ts index a863c2132ae..a2a906f69fe 100644 --- a/packages/core/injector/internal-core-module/index.ts +++ b/packages/core/injector/internal-core-module/index.ts @@ -1 +1 @@ -export * from './internal-core-module'; +export * from './internal-core-module.js'; diff --git a/packages/core/injector/internal-core-module/internal-core-module-factory.ts b/packages/core/injector/internal-core-module/internal-core-module-factory.ts index 9c3533049c0..ecbba528b1e 100644 --- a/packages/core/injector/internal-core-module/internal-core-module-factory.ts +++ b/packages/core/injector/internal-core-module/internal-core-module-factory.ts @@ -1,18 +1,18 @@ import { Logger } from '@nestjs/common'; -import { ExternalContextCreator } from '../../helpers/external-context-creator'; -import { HttpAdapterHost } from '../../helpers/http-adapter-host'; -import { GraphInspector } from '../../inspector/graph-inspector'; -import { InitializeOnPreviewAllowlist } from '../../inspector/initialize-on-preview.allowlist'; -import { SerializedGraph } from '../../inspector/serialized-graph'; -import { ModuleOverride } from '../../interfaces/module-override.interface'; -import { DependenciesScanner } from '../../scanner'; -import { ModuleCompiler } from '../compiler'; -import { NestContainer } from '../container'; -import { Injector } from '../injector'; -import { InstanceLoader } from '../instance-loader'; -import { LazyModuleLoader } from '../lazy-module-loader/lazy-module-loader'; -import { ModulesContainer } from '../modules-container'; -import { InternalCoreModule } from './internal-core-module'; +import { ExternalContextCreator } from '../../helpers/external-context-creator.js'; +import { HttpAdapterHost } from '../../helpers/http-adapter-host.js'; +import { GraphInspector } from '../../inspector/graph-inspector.js'; +import { InitializeOnPreviewAllowlist } from '../../inspector/initialize-on-preview.allowlist.js'; +import { SerializedGraph } from '../../inspector/serialized-graph.js'; +import { ModuleOverride } from '../../interfaces/module-override.interface.js'; +import { DependenciesScanner } from '../../scanner.js'; +import { ModuleCompiler } from '../compiler.js'; +import { NestContainer } from '../container.js'; +import { Injector } from '../injector.js'; +import { InstanceLoader } from '../instance-loader.js'; +import { LazyModuleLoader } from '../lazy-module-loader/lazy-module-loader.js'; +import { ModulesContainer } from '../modules-container.js'; +import { InternalCoreModule } from './internal-core-module.js'; export class InternalCoreModuleFactory { static create( diff --git a/packages/core/injector/internal-core-module/internal-core-module.ts b/packages/core/injector/internal-core-module/internal-core-module.ts index 1788183377b..f27c59fceeb 100644 --- a/packages/core/injector/internal-core-module/internal-core-module.ts +++ b/packages/core/injector/internal-core-module/internal-core-module.ts @@ -1,12 +1,12 @@ -import { DynamicModule, Global, Module } from '@nestjs/common'; -import { +import { type DynamicModule, Global, Module } from '@nestjs/common'; +import { requestProvider } from '../../router/request/request-providers.js'; +import { Reflector } from '../../services/index.js'; +import { inquirerProvider } from '../inquirer/inquirer-providers.js'; +import type { ExistingProvider, FactoryProvider, ValueProvider, -} from '@nestjs/common/interfaces'; -import { requestProvider } from '../../router/request/request-providers'; -import { Reflector } from '../../services'; -import { inquirerProvider } from '../inquirer/inquirer-providers'; +} from '@nestjs/common'; const ReflectorAliasProvider = { provide: Reflector.name, diff --git a/packages/core/injector/internal-providers-storage.ts b/packages/core/injector/internal-providers-storage.ts index 952e3697783..61f6bbd6db1 100644 --- a/packages/core/injector/internal-providers-storage.ts +++ b/packages/core/injector/internal-providers-storage.ts @@ -1,5 +1,5 @@ -import { AbstractHttpAdapter } from '../adapters'; -import { HttpAdapterHost } from '../helpers/http-adapter-host'; +import { AbstractHttpAdapter } from '../adapters/index.js'; +import { HttpAdapterHost } from '../helpers/http-adapter-host.js'; export class InternalProvidersStorage { private readonly _httpAdapterHost = new HttpAdapterHost(); diff --git a/packages/core/injector/lazy-module-loader/lazy-module-loader.ts b/packages/core/injector/lazy-module-loader/lazy-module-loader.ts index b949a1b74cc..018cdcd01f3 100644 --- a/packages/core/injector/lazy-module-loader/lazy-module-loader.ts +++ b/packages/core/injector/lazy-module-loader/lazy-module-loader.ts @@ -1,13 +1,13 @@ -import { DynamicModule, Type } from '@nestjs/common'; -import { ModuleOverride } from '../../interfaces/module-override.interface'; -import { DependenciesScanner } from '../../scanner'; -import { ModuleCompiler } from '../compiler'; -import { SilentLogger } from '../helpers/silent-logger'; -import { InstanceLoader } from '../instance-loader'; -import { Module } from '../module'; -import { ModuleRef } from '../module-ref'; -import { ModulesContainer } from '../modules-container'; -import { LazyModuleLoaderLoadOptions } from './lazy-module-loader-options.interface'; +import type { DynamicModule, Type } from '@nestjs/common'; +import { ModuleOverride } from '../../interfaces/module-override.interface.js'; +import { DependenciesScanner } from '../../scanner.js'; +import { ModuleCompiler } from '../compiler.js'; +import { SilentLogger } from '../helpers/silent-logger.js'; +import { InstanceLoader } from '../instance-loader.js'; +import { Module } from '../module.js'; +import { ModuleRef } from '../module-ref.js'; +import { ModulesContainer } from '../modules-container.js'; +import { LazyModuleLoaderLoadOptions } from './lazy-module-loader-options.interface.js'; export class LazyModuleLoader { constructor( diff --git a/packages/core/injector/module-ref.ts b/packages/core/injector/module-ref.ts index 6dd3544181f..332062665f0 100644 --- a/packages/core/injector/module-ref.ts +++ b/packages/core/injector/module-ref.ts @@ -1,12 +1,12 @@ -import { IntrospectionResult, Scope, Type } from '@nestjs/common'; -import { getClassScope } from '../helpers/get-class-scope'; -import { isDurable } from '../helpers/is-durable'; -import { AbstractInstanceResolver } from './abstract-instance-resolver'; -import { NestContainer } from './container'; -import { Injector } from './injector'; -import { InstanceLinksHost } from './instance-links-host'; -import { ContextId, InstanceWrapper } from './instance-wrapper'; -import { Module } from './module'; +import { type IntrospectionResult, Scope, type Type } from '@nestjs/common'; +import { getClassScope } from '../helpers/get-class-scope.js'; +import { isDurable } from '../helpers/is-durable.js'; +import { AbstractInstanceResolver } from './abstract-instance-resolver.js'; +import { NestContainer } from './container.js'; +import { Injector } from './injector.js'; +import { InstanceLinksHost } from './instance-links-host.js'; +import { ContextId, InstanceWrapper } from './instance-wrapper.js'; +import { Module } from './module.js'; export interface ModuleRefGetOrResolveOpts { /** diff --git a/packages/core/injector/module.ts b/packages/core/injector/module.ts index c9dd10a937c..95f462ea235 100644 --- a/packages/core/injector/module.ts +++ b/packages/core/injector/module.ts @@ -1,45 +1,43 @@ +import { iterate } from 'iterare'; +import { ApplicationConfig } from '../application-config.js'; import { - EnhancerSubtype, - ENTRY_PROVIDER_WATERMARK, -} from '@nestjs/common/constants'; -import { - ClassProvider, - Controller, - DynamicModule, - ExistingProvider, - FactoryProvider, - Injectable, - InjectionToken, - NestModule, - Provider, - Scope, - Type, - ValueProvider, -} from '@nestjs/common/interfaces'; -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; + InvalidClassException, + RuntimeException, + UnknownExportException, +} from '../errors/exceptions/index.js'; +import { createContextId } from '../helpers/context-id-factory.js'; +import { getClassScope } from '../helpers/get-class-scope.js'; +import { isDurable } from '../helpers/is-durable.js'; +import { UuidFactory } from '../inspector/uuid-factory.js'; +import { CONTROLLER_ID_KEY } from './constants.js'; +import { NestContainer } from './container.js'; +import { ContextId, InstanceWrapper } from './instance-wrapper.js'; +import { ModuleRef, ModuleRefGetOrResolveOpts } from './module-ref.js'; import { + type EnhancerSubtype, + ENTRY_PROVIDER_WATERMARK, + type Controller, + type Injectable, + randomStringGenerator, isFunction, isNil, isObject, isString, isSymbol, isUndefined, -} from '@nestjs/common/utils/shared.utils'; -import { iterate } from 'iterare'; -import { ApplicationConfig } from '../application-config'; +} from '@nestjs/common/internal'; import { - InvalidClassException, - RuntimeException, - UnknownExportException, -} from '../errors/exceptions'; -import { createContextId } from '../helpers/context-id-factory'; -import { getClassScope } from '../helpers/get-class-scope'; -import { isDurable } from '../helpers/is-durable'; -import { UuidFactory } from '../inspector/uuid-factory'; -import { CONTROLLER_ID_KEY } from './constants'; -import { NestContainer } from './container'; -import { ContextId, InstanceWrapper } from './instance-wrapper'; -import { ModuleRef, ModuleRefGetOrResolveOpts } from './module-ref'; + type ClassProvider, + type DynamicModule, + type ExistingProvider, + type FactoryProvider, + type InjectionToken, + type NestModule, + type Provider, + Scope, + type Type, + type ValueProvider, +} from '@nestjs/common'; export class Module { private readonly _id: string; @@ -564,33 +562,37 @@ export class Module { } public getProviderById(id: string): InstanceWrapper | undefined { - return Array.from(this._providers.values()).find( - item => item.id === id, - ) as InstanceWrapper; + for (const item of this._providers.values()) { + if (item.id === id) return item as InstanceWrapper; + } + return undefined; } public getControllerById( id: string, ): InstanceWrapper | undefined { - return Array.from(this._controllers.values()).find( - item => item.id === id, - ) as InstanceWrapper; + for (const item of this._controllers.values()) { + if (item.id === id) return item as InstanceWrapper; + } + return undefined; } public getInjectableById( id: string, ): InstanceWrapper | undefined { - return Array.from(this._injectables.values()).find( - item => item.id === id, - ) as InstanceWrapper; + for (const item of this._injectables.values()) { + if (item.id === id) return item as InstanceWrapper; + } + return undefined; } public getMiddlewareById( id: string, ): InstanceWrapper | undefined { - return Array.from(this._middlewares.values()).find( - item => item.id === id, - ) as InstanceWrapper; + for (const item of this._middlewares.values()) { + if (item.id === id) return item as InstanceWrapper; + } + return undefined; } public getNonAliasProviders(): Array< diff --git a/packages/core/injector/modules-container.ts b/packages/core/injector/modules-container.ts index c2316f5cb98..3343f72b055 100644 --- a/packages/core/injector/modules-container.ts +++ b/packages/core/injector/modules-container.ts @@ -1,6 +1,6 @@ import { Observable, ReplaySubject } from 'rxjs'; import { uid } from 'uid'; -import { Module } from './module'; +import { Module } from './module.js'; export class ModulesContainer extends Map { private readonly _applicationId = uid(21); diff --git a/packages/core/injector/opaque-key-factory/by-reference-module-opaque-key-factory.ts b/packages/core/injector/opaque-key-factory/by-reference-module-opaque-key-factory.ts index e0a6971e158..0d106c894ba 100644 --- a/packages/core/injector/opaque-key-factory/by-reference-module-opaque-key-factory.ts +++ b/packages/core/injector/opaque-key-factory/by-reference-module-opaque-key-factory.ts @@ -1,9 +1,7 @@ -import { DynamicModule } from '@nestjs/common/interfaces/modules/dynamic-module.interface'; -import { ForwardReference } from '@nestjs/common/interfaces/modules/forward-reference.interface'; -import { Type } from '@nestjs/common/interfaces/type.interface'; -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; import { createHash } from 'crypto'; -import { ModuleOpaqueKeyFactory } from './interfaces/module-opaque-key-factory.interface'; +import { ModuleOpaqueKeyFactory } from './interfaces/module-opaque-key-factory.interface.js'; +import type { DynamicModule, ForwardReference, Type } from '@nestjs/common'; +import { randomStringGenerator } from '@nestjs/common/internal'; const K_MODULE_ID = Symbol('K_MODULE_ID'); diff --git a/packages/core/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.ts b/packages/core/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.ts index 010938732f4..755ac63242c 100644 --- a/packages/core/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.ts +++ b/packages/core/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.ts @@ -1,11 +1,18 @@ -import { DynamicModule } from '@nestjs/common/interfaces/modules/dynamic-module.interface'; -import { Type } from '@nestjs/common/interfaces/type.interface'; -import { Logger } from '@nestjs/common/services/logger.service'; -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; -import { isFunction, isSymbol } from '@nestjs/common/utils/shared.utils'; import { createHash } from 'crypto'; -import stringify from 'fast-safe-stringify'; -import { ModuleOpaqueKeyFactory } from './interfaces/module-opaque-key-factory.interface'; +import _stringify from 'fast-safe-stringify'; +import { ModuleOpaqueKeyFactory } from './interfaces/module-opaque-key-factory.interface.js'; +import { type DynamicModule, type Type, Logger } from '@nestjs/common'; +import { + randomStringGenerator, + isFunction, + isSymbol, +} from '@nestjs/common/internal'; +// CJS interop: fast-safe-stringify sets module.exports.default = module.exports +const stringify = ((_stringify as any).default ?? _stringify) as unknown as ( + value: any, + replacer?: (key: string, value: any) => any, + space?: string | number, +) => string; const CLASS_STR = 'class '; const CLASS_STR_LEN = CLASS_STR.length; diff --git a/packages/core/injector/opaque-key-factory/interfaces/module-opaque-key-factory.interface.ts b/packages/core/injector/opaque-key-factory/interfaces/module-opaque-key-factory.interface.ts index 1b58a5fffdc..f2cf80a9d32 100644 --- a/packages/core/injector/opaque-key-factory/interfaces/module-opaque-key-factory.interface.ts +++ b/packages/core/injector/opaque-key-factory/interfaces/module-opaque-key-factory.interface.ts @@ -1,6 +1,4 @@ -import { DynamicModule } from '@nestjs/common/interfaces/modules/dynamic-module.interface'; -import { ForwardReference } from '@nestjs/common/interfaces/modules/forward-reference.interface'; -import { Type } from '@nestjs/common/interfaces/type.interface'; +import type { DynamicModule, ForwardReference, Type } from '@nestjs/common'; export interface ModuleOpaqueKeyFactory { /** diff --git a/packages/core/injector/topology-tree/topology-tree.ts b/packages/core/injector/topology-tree/topology-tree.ts index 940cd4aa398..6f06d1fe1f6 100644 --- a/packages/core/injector/topology-tree/topology-tree.ts +++ b/packages/core/injector/topology-tree/topology-tree.ts @@ -1,5 +1,5 @@ -import { Module } from '../module'; -import { TreeNode } from './tree-node'; +import { Module } from '../module.js'; +import { TreeNode } from './tree-node.js'; export class TopologyTree { private root: TreeNode; diff --git a/packages/core/inspector/graph-inspector.ts b/packages/core/inspector/graph-inspector.ts index bf79028a6db..c353bd8b5b8 100644 --- a/packages/core/inspector/graph-inspector.ts +++ b/packages/core/inspector/graph-inspector.ts @@ -1,14 +1,14 @@ -import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; -import { DeterministicUuidRegistry } from './deterministic-uuid-registry'; -import { EnhancerMetadataCacheEntry } from './interfaces/enhancer-metadata-cache-entry.interface'; -import { Entrypoint } from './interfaces/entrypoint.interface'; -import { OrphanedEnhancerDefinition } from './interfaces/extras.interface'; -import { ClassNode, Node } from './interfaces/node.interface'; -import { PartialGraphHost } from './partial-graph.host'; -import { SerializedGraph } from './serialized-graph'; +import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { Module } from '../injector/module.js'; +import { DeterministicUuidRegistry } from './deterministic-uuid-registry.js'; +import { EnhancerMetadataCacheEntry } from './interfaces/enhancer-metadata-cache-entry.interface.js'; +import { Entrypoint } from './interfaces/entrypoint.interface.js'; +import { OrphanedEnhancerDefinition } from './interfaces/extras.interface.js'; +import { ClassNode, Node } from './interfaces/node.interface.js'; +import { PartialGraphHost } from './partial-graph.host.js'; +import { SerializedGraph } from './serialized-graph.js'; export class GraphInspector { private readonly graph: SerializedGraph; diff --git a/packages/core/inspector/index.ts b/packages/core/inspector/index.ts index db830f6e711..dc01ab9b4b7 100644 --- a/packages/core/inspector/index.ts +++ b/packages/core/inspector/index.ts @@ -1,4 +1,4 @@ -export * from './graph-inspector'; -export * from './initialize-on-preview.allowlist'; -export * from './partial-graph.host'; -export * from './serialized-graph'; +export * from './graph-inspector.js'; +export * from './initialize-on-preview.allowlist.js'; +export * from './partial-graph.host.js'; +export * from './serialized-graph.js'; diff --git a/packages/core/inspector/initialize-on-preview.allowlist.ts b/packages/core/inspector/initialize-on-preview.allowlist.ts index 27d6ad593a6..b3c97b86ff8 100644 --- a/packages/core/inspector/initialize-on-preview.allowlist.ts +++ b/packages/core/inspector/initialize-on-preview.allowlist.ts @@ -1,4 +1,4 @@ -import { Type } from '@nestjs/common'; +import type { Type } from '@nestjs/common'; export class InitializeOnPreviewAllowlist { private static readonly allowlist = new WeakMap(); diff --git a/packages/core/inspector/interfaces/edge.interface.ts b/packages/core/inspector/interfaces/edge.interface.ts index 9151ab43b3a..a215a4115ed 100644 --- a/packages/core/inspector/interfaces/edge.interface.ts +++ b/packages/core/inspector/interfaces/edge.interface.ts @@ -1,4 +1,4 @@ -import { InjectionToken } from '@nestjs/common'; +import type { InjectionToken } from '@nestjs/common'; type CommonEdgeMetadata = { sourceModuleName: string; diff --git a/packages/core/inspector/interfaces/enhancer-metadata-cache-entry.interface.ts b/packages/core/inspector/interfaces/enhancer-metadata-cache-entry.interface.ts index 7f4574d94ea..2b815cd3ec8 100644 --- a/packages/core/inspector/interfaces/enhancer-metadata-cache-entry.interface.ts +++ b/packages/core/inspector/interfaces/enhancer-metadata-cache-entry.interface.ts @@ -1,6 +1,6 @@ -import { Type } from '@nestjs/common'; -import { EnhancerSubtype } from '@nestjs/common/constants'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; +import type { Type } from '@nestjs/common'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import type { EnhancerSubtype } from '@nestjs/common/internal'; export interface EnhancerMetadataCacheEntry { targetNodeId?: string; diff --git a/packages/core/inspector/interfaces/entrypoint.interface.ts b/packages/core/inspector/interfaces/entrypoint.interface.ts index 057c040c36d..866523aef04 100644 --- a/packages/core/inspector/interfaces/entrypoint.interface.ts +++ b/packages/core/inspector/interfaces/entrypoint.interface.ts @@ -1,5 +1,5 @@ -import { RequestMethod } from '@nestjs/common'; -import { VersionValue } from '@nestjs/common/interfaces'; +import type { RequestMethod } from '@nestjs/common'; +import type { VersionValue } from '@nestjs/common/internal'; export type HttpEntrypointMetadata = { path: string; diff --git a/packages/core/inspector/interfaces/extras.interface.ts b/packages/core/inspector/interfaces/extras.interface.ts index 37abd639f9c..00fab1eecfa 100644 --- a/packages/core/inspector/interfaces/extras.interface.ts +++ b/packages/core/inspector/interfaces/extras.interface.ts @@ -1,4 +1,4 @@ -import { EnhancerSubtype } from '@nestjs/common/constants'; +import type { EnhancerSubtype } from '@nestjs/common/internal'; /** * Enhancers attached through APP_PIPE, APP_GUARD, APP_INTERCEPTOR, and APP_FILTER tokens. diff --git a/packages/core/inspector/interfaces/node.interface.ts b/packages/core/inspector/interfaces/node.interface.ts index 1d85893990a..81c6cc9f1bd 100644 --- a/packages/core/inspector/interfaces/node.interface.ts +++ b/packages/core/inspector/interfaces/node.interface.ts @@ -1,5 +1,5 @@ -import { InjectionToken, Scope } from '@nestjs/common'; -import { EnhancerSubtype } from '@nestjs/common/constants'; +import type { InjectionToken, Scope } from '@nestjs/common'; +import type { EnhancerSubtype } from '@nestjs/common/internal'; export type ModuleNode = { metadata: { diff --git a/packages/core/inspector/interfaces/serialized-graph-json.interface.ts b/packages/core/inspector/interfaces/serialized-graph-json.interface.ts index 9cd9d76cf43..e55a5850147 100644 --- a/packages/core/inspector/interfaces/serialized-graph-json.interface.ts +++ b/packages/core/inspector/interfaces/serialized-graph-json.interface.ts @@ -1,9 +1,9 @@ -import { SerializedGraphStatus } from '../serialized-graph'; -import { Edge } from './edge.interface'; -import { Entrypoint } from './entrypoint.interface'; -import { Extras } from './extras.interface'; -import { Node } from './node.interface'; -import { SerializedGraphMetadata } from './serialized-graph-metadata.interface'; +import { SerializedGraphStatus } from '../serialized-graph.js'; +import { Edge } from './edge.interface.js'; +import { Entrypoint } from './entrypoint.interface.js'; +import { Extras } from './extras.interface.js'; +import { Node } from './node.interface.js'; +import { SerializedGraphMetadata } from './serialized-graph-metadata.interface.js'; export interface SerializedGraphJson { nodes: Record; diff --git a/packages/core/inspector/interfaces/serialized-graph-metadata.interface.ts b/packages/core/inspector/interfaces/serialized-graph-metadata.interface.ts index c88749d0195..dedf0fe565a 100644 --- a/packages/core/inspector/interfaces/serialized-graph-metadata.interface.ts +++ b/packages/core/inspector/interfaces/serialized-graph-metadata.interface.ts @@ -1,4 +1,4 @@ -import { InjectorDependencyContext } from '../../injector/injector'; +import { InjectorDependencyContext } from '../../injector/injector.js'; export interface SerializedGraphMetadata { cause: { diff --git a/packages/core/inspector/noop-graph-inspector.ts b/packages/core/inspector/noop-graph-inspector.ts index ba48ce5d833..260d122c2b9 100644 --- a/packages/core/inspector/noop-graph-inspector.ts +++ b/packages/core/inspector/noop-graph-inspector.ts @@ -1,4 +1,4 @@ -import { GraphInspector } from './graph-inspector'; +import { GraphInspector } from './graph-inspector.js'; const noop = () => {}; export const NoopGraphInspector: GraphInspector = new Proxy( diff --git a/packages/core/inspector/partial-graph.host.ts b/packages/core/inspector/partial-graph.host.ts index 84ae71f514c..72ff8163847 100644 --- a/packages/core/inspector/partial-graph.host.ts +++ b/packages/core/inspector/partial-graph.host.ts @@ -1,4 +1,4 @@ -import { SerializedGraph } from './serialized-graph'; +import { SerializedGraph } from './serialized-graph.js'; export class PartialGraphHost { private static partialGraph: SerializedGraph; diff --git a/packages/core/inspector/serialized-graph.ts b/packages/core/inspector/serialized-graph.ts index a4271620865..1cd0fac6318 100644 --- a/packages/core/inspector/serialized-graph.ts +++ b/packages/core/inspector/serialized-graph.ts @@ -1,23 +1,23 @@ -import { InjectionToken } from '@nestjs/common'; -import { ApplicationConfig } from '../application-config'; -import { ExternalContextCreator } from '../helpers/external-context-creator'; -import { HttpAdapterHost } from '../helpers/http-adapter-host'; -import { INQUIRER } from '../injector/inquirer/inquirer-constants'; -import { LazyModuleLoader } from '../injector/lazy-module-loader/lazy-module-loader'; -import { ModuleRef } from '../injector/module-ref'; -import { ModulesContainer } from '../injector/modules-container'; -import { REQUEST } from '../router/request/request-constants'; -import { Reflector } from '../services/reflector.service'; -import { DeterministicUuidRegistry } from './deterministic-uuid-registry'; -import { Edge } from './interfaces/edge.interface'; -import { Entrypoint } from './interfaces/entrypoint.interface'; +import type { InjectionToken } from '@nestjs/common'; +import { ApplicationConfig } from '../application-config.js'; +import { ExternalContextCreator } from '../helpers/external-context-creator.js'; +import { HttpAdapterHost } from '../helpers/http-adapter-host.js'; +import { INQUIRER } from '../injector/inquirer/inquirer-constants.js'; +import { LazyModuleLoader } from '../injector/lazy-module-loader/lazy-module-loader.js'; +import { ModuleRef } from '../injector/module-ref.js'; +import { ModulesContainer } from '../injector/modules-container.js'; +import { REQUEST } from '../router/request/request-constants.js'; +import { Reflector } from '../services/reflector.service.js'; +import { DeterministicUuidRegistry } from './deterministic-uuid-registry.js'; +import { Edge } from './interfaces/edge.interface.js'; +import { Entrypoint } from './interfaces/entrypoint.interface.js'; import { Extras, OrphanedEnhancerDefinition, -} from './interfaces/extras.interface'; -import { Node } from './interfaces/node.interface'; -import { SerializedGraphJson } from './interfaces/serialized-graph-json.interface'; -import { SerializedGraphMetadata } from './interfaces/serialized-graph-metadata.interface'; +} from './interfaces/extras.interface.js'; +import { Node } from './interfaces/node.interface.js'; +import { SerializedGraphJson } from './interfaces/serialized-graph-json.interface.js'; +import { SerializedGraphMetadata } from './interfaces/serialized-graph-metadata.interface.js'; export type SerializedGraphStatus = 'partial' | 'complete'; type WithOptionalId> = Omit & diff --git a/packages/core/inspector/uuid-factory.ts b/packages/core/inspector/uuid-factory.ts index 1f89ad13d42..3346d3a34fb 100644 --- a/packages/core/inspector/uuid-factory.ts +++ b/packages/core/inspector/uuid-factory.ts @@ -1,5 +1,5 @@ -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; -import { DeterministicUuidRegistry } from './deterministic-uuid-registry'; +import { DeterministicUuidRegistry } from './deterministic-uuid-registry.js'; +import { randomStringGenerator } from '@nestjs/common/internal'; export enum UuidFactoryMode { Random = 'random', diff --git a/packages/core/interceptors/index.ts b/packages/core/interceptors/index.ts index 7835bb29b47..cdb8e52fc3a 100644 --- a/packages/core/interceptors/index.ts +++ b/packages/core/interceptors/index.ts @@ -1,2 +1,2 @@ -export * from './interceptors-consumer'; -export * from './interceptors-context-creator'; +export * from './interceptors-consumer.js'; +export * from './interceptors-context-creator.js'; diff --git a/packages/core/interceptors/interceptors-consumer.ts b/packages/core/interceptors/interceptors-consumer.ts index 5032e93235a..9104c7106ba 100644 --- a/packages/core/interceptors/interceptors-consumer.ts +++ b/packages/core/interceptors/interceptors-consumer.ts @@ -1,14 +1,10 @@ -import { NestInterceptor, Type } from '@nestjs/common'; -import { - CallHandler, - ContextType, - Controller, -} from '@nestjs/common/interfaces'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; +import type { NestInterceptor, Type } from '@nestjs/common'; import { AsyncResource } from 'async_hooks'; import { Observable, defer, from as fromPromise } from 'rxjs'; import { mergeAll, switchMap } from 'rxjs/operators'; -import { ExecutionContextHost } from '../helpers/execution-context-host'; +import { ExecutionContextHost } from '../helpers/execution-context-host.js'; +import type { CallHandler, ContextType } from '@nestjs/common'; +import { type Controller, isEmptyArray } from '@nestjs/common/internal'; export class InterceptorsConsumer { public async intercept( @@ -19,7 +15,7 @@ export class InterceptorsConsumer { next: () => Promise, type?: TContext, ): Promise { - if (isEmpty(interceptors)) { + if (isEmptyArray(interceptors)) { return next(); } const context = this.createContext(args, instance, callback); diff --git a/packages/core/interceptors/interceptors-context-creator.ts b/packages/core/interceptors/interceptors-context-creator.ts index a7bebc04498..db94f7c3804 100644 --- a/packages/core/interceptors/interceptors-context-creator.ts +++ b/packages/core/interceptors/interceptors-context-creator.ts @@ -1,12 +1,16 @@ -import { INTERCEPTORS_METADATA } from '@nestjs/common/constants'; -import { Controller, NestInterceptor, Type } from '@nestjs/common/interfaces'; -import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils'; import { iterate } from 'iterare'; -import { ApplicationConfig } from '../application-config'; -import { ContextCreator } from '../helpers/context-creator'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; +import { ApplicationConfig } from '../application-config.js'; +import { ContextCreator } from '../helpers/context-creator.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { + INTERCEPTORS_METADATA, + type Controller, + isEmpty, + isFunction, +} from '@nestjs/common/internal'; +import type { NestInterceptor, Type } from '@nestjs/common'; export class InterceptorsContextCreator extends ContextCreator { private moduleContext: string; diff --git a/packages/core/interfaces/index.ts b/packages/core/interfaces/index.ts new file mode 100644 index 00000000000..448bd8e425c --- /dev/null +++ b/packages/core/interfaces/index.ts @@ -0,0 +1,2 @@ +export * from './module-definition.interface.js'; +export * from './module-override.interface.js'; diff --git a/packages/core/interfaces/module-definition.interface.ts b/packages/core/interfaces/module-definition.interface.ts index edeb8d60e87..ea709226433 100644 --- a/packages/core/interfaces/module-definition.interface.ts +++ b/packages/core/interfaces/module-definition.interface.ts @@ -1,5 +1,5 @@ -import { DynamicModule, ForwardReference } from '@nestjs/common'; -import { Type } from '@nestjs/common/interfaces'; +import type { DynamicModule, ForwardReference } from '@nestjs/common'; +import type { Type } from '@nestjs/common'; export type ModuleDefinition = | ForwardReference diff --git a/packages/core/interfaces/module-override.interface.ts b/packages/core/interfaces/module-override.interface.ts index c92dffb2d36..cee2602ae7f 100644 --- a/packages/core/interfaces/module-override.interface.ts +++ b/packages/core/interfaces/module-override.interface.ts @@ -1,4 +1,4 @@ -import { ModuleDefinition } from './module-definition.interface'; +import { ModuleDefinition } from './module-definition.interface.js'; export interface ModuleOverride { moduleToReplace: ModuleDefinition; diff --git a/packages/core/internal.ts b/packages/core/internal.ts new file mode 100644 index 00000000000..3e90a9e5225 --- /dev/null +++ b/packages/core/internal.ts @@ -0,0 +1,64 @@ +/** + * Internal module - not part of the public API. + * These exports are used by sibling @nestjs packages. + * Do not depend on these in your application code. + * @internal + * @module + */ + +// Errors +export { RuntimeException } from './errors/exceptions/runtime.exception.js'; +export { InvalidExceptionFilterException } from './errors/exceptions/invalid-exception-filter.exception.js'; + +// Constants +export { MESSAGES } from './constants.js'; + +// Scanner +export { DependenciesScanner } from './scanner.js'; + +// Injector +export { Injector, InjectorDependencyContext } from './injector/injector.js'; +export { InstanceLoader } from './injector/instance-loader.js'; +export { InstanceWrapper } from './injector/instance-wrapper.js'; +export { Module } from './injector/module.js'; +export { STATIC_CONTEXT } from './injector/constants.js'; + +// Helpers +export { ExecutionContextHost } from './helpers/execution-context-host.js'; +export { ContextUtils, ParamProperties } from './helpers/context-utils.js'; +export { HandlerMetadataStorage } from './helpers/handler-metadata-storage.js'; +export { RouterMethodFactory } from './helpers/router-method-factory.js'; +export { loadAdapter } from './helpers/load-adapter.js'; +export { optionalRequire } from './helpers/optional-require.js'; + +// Helpers - interfaces +export { ParamsMetadata } from './helpers/interfaces/index.js'; + +// Guards +export { GuardsConsumer } from './guards/guards-consumer.js'; +export { GuardsContextCreator } from './guards/guards-context-creator.js'; +export { FORBIDDEN_MESSAGE } from './guards/constants.js'; + +// Pipes +export { PipesConsumer } from './pipes/pipes-consumer.js'; +export { PipesContextCreator } from './pipes/pipes-context-creator.js'; +export { ParamsTokenFactory } from './pipes/params-token-factory.js'; + +// Interceptors +export { InterceptorsConsumer } from './interceptors/interceptors-consumer.js'; +export { InterceptorsContextCreator } from './interceptors/interceptors-context-creator.js'; + +// Exceptions +export { BaseExceptionFilterContext } from './exceptions/base-exception-filter-context.js'; + +// Router +export { LegacyRouteConverter } from './router/legacy-route-converter.js'; +export { REQUEST_CONTEXT_ID } from './router/request/request-constants.js'; + +// Inspector +export { NoopGraphInspector } from './inspector/noop-graph-inspector.js'; +export { UuidFactory, UuidFactoryMode } from './inspector/uuid-factory.js'; + +// Interfaces +export { ModuleDefinition } from './interfaces/module-definition.interface.js'; +export { ModuleOverride } from './interfaces/module-override.interface.js'; diff --git a/packages/core/metadata-scanner.ts b/packages/core/metadata-scanner.ts index 7c6a2326663..d0766d485d2 100644 --- a/packages/core/metadata-scanner.ts +++ b/packages/core/metadata-scanner.ts @@ -1,9 +1,9 @@ -import { Injectable } from '@nestjs/common/interfaces/injectable.interface'; import { + type Injectable, isConstructor, isFunction, isNil, -} from '@nestjs/common/utils/shared.utils'; +} from '@nestjs/common/internal'; export class MetadataScanner { private readonly cachedScannedPrototypes: Map = new Map(); diff --git a/packages/core/middleware/builder.ts b/packages/core/middleware/builder.ts index 3d423e16010..bbc7a9858f6 100644 --- a/packages/core/middleware/builder.ts +++ b/packages/core/middleware/builder.ts @@ -1,18 +1,14 @@ -import { - HttpServer, - MiddlewareConsumer, - Type, -} from '@nestjs/common/interfaces'; -import { - MiddlewareConfigProxy, - MiddlewareConfiguration, - RouteInfo, -} from '@nestjs/common/interfaces/middleware'; -import { stripEndSlash } from '@nestjs/common/utils/shared.utils'; import { iterate } from 'iterare'; -import { RouteInfoPathExtractor } from './route-info-path-extractor'; -import { RoutesMapper } from './routes-mapper'; -import { filterMiddleware } from './utils'; +import { RouteInfoPathExtractor } from './route-info-path-extractor.js'; +import { RoutesMapper } from './routes-mapper.js'; +import { filterMiddleware } from './utils.js'; +import type { HttpServer, MiddlewareConsumer, Type } from '@nestjs/common'; +import { + type MiddlewareConfigProxy, + type MiddlewareConfiguration, + type RouteInfo, + stripEndSlash, +} from '@nestjs/common/internal'; export class MiddlewareBuilder implements MiddlewareConsumer { private readonly middlewareCollection = new Set(); diff --git a/packages/core/middleware/container.ts b/packages/core/middleware/container.ts index a30a3fa2d80..bde18bb0b2f 100644 --- a/packages/core/middleware/container.ts +++ b/packages/core/middleware/container.ts @@ -1,9 +1,9 @@ -import { InjectionToken, Type } from '@nestjs/common'; -import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface'; -import { getClassScope } from '../helpers/get-class-scope'; -import { isDurable } from '../helpers/is-durable'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; +import type { InjectionToken, Type } from '@nestjs/common'; +import { getClassScope } from '../helpers/get-class-scope.js'; +import { isDurable } from '../helpers/is-durable.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import type { MiddlewareConfiguration } from '@nestjs/common/internal'; export class MiddlewareContainer { private readonly middleware = new Map< diff --git a/packages/core/middleware/index.ts b/packages/core/middleware/index.ts index ecea700bcde..2d7a031ef70 100644 --- a/packages/core/middleware/index.ts +++ b/packages/core/middleware/index.ts @@ -1 +1 @@ -export * from './builder'; +export * from './builder.js'; diff --git a/packages/core/middleware/middleware-module.ts b/packages/core/middleware/middleware-module.ts index 3e4e8c3ba22..c4281f82fb4 100644 --- a/packages/core/middleware/middleware-module.ts +++ b/packages/core/middleware/middleware-module.ts @@ -1,36 +1,35 @@ -import { HttpServer, InjectionToken, Logger } from '@nestjs/common'; -import { RequestMethod } from '@nestjs/common/enums/request-method.enum'; -import { - MiddlewareConfiguration, - NestMiddleware, - RouteInfo, -} from '@nestjs/common/interfaces/middleware'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { isUndefined } from '@nestjs/common/utils/shared.utils'; -import { ApplicationConfig } from '../application-config'; -import { InvalidMiddlewareException } from '../errors/exceptions/invalid-middleware.exception'; -import { RuntimeException } from '../errors/exceptions/runtime.exception'; -import { ContextIdFactory } from '../helpers/context-id-factory'; -import { ExecutionContextHost } from '../helpers/execution-context-host'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { Injector } from '../injector/injector'; -import { ContextId, InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; -import { GraphInspector } from '../inspector/graph-inspector'; +import { type HttpServer, type InjectionToken, Logger } from '@nestjs/common'; +import { ApplicationConfig } from '../application-config.js'; +import { InvalidMiddlewareException } from '../errors/exceptions/invalid-middleware.exception.js'; +import { RuntimeException } from '../errors/exceptions/runtime.exception.js'; +import { ContextIdFactory } from '../helpers/context-id-factory.js'; +import { ExecutionContextHost } from '../helpers/execution-context-host.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { Injector } from '../injector/injector.js'; +import { ContextId, InstanceWrapper } from '../injector/instance-wrapper.js'; +import { Module } from '../injector/module.js'; +import { GraphInspector } from '../inspector/graph-inspector.js'; import { Entrypoint, MiddlewareEntrypointMetadata, -} from '../inspector/interfaces/entrypoint.interface'; -import { REQUEST_CONTEXT_ID } from '../router/request/request-constants'; -import { RouterExceptionFilters } from '../router/router-exception-filters'; -import { RouterProxy } from '../router/router-proxy'; -import { isRequestMethodAll } from '../router/utils'; -import { MiddlewareBuilder } from './builder'; -import { MiddlewareContainer } from './container'; -import { MiddlewareResolver } from './resolver'; -import { RouteInfoPathExtractor } from './route-info-path-extractor'; -import { RoutesMapper } from './routes-mapper'; +} from '../inspector/interfaces/entrypoint.interface.js'; +import { REQUEST_CONTEXT_ID } from '../router/request/request-constants.js'; +import { RouterExceptionFilters } from '../router/router-exception-filters.js'; +import { RouterProxy } from '../router/router-proxy.js'; +import { isRequestMethodAll } from '../router/utils/index.js'; +import { MiddlewareBuilder } from './builder.js'; +import { MiddlewareContainer } from './container.js'; +import { MiddlewareResolver } from './resolver.js'; +import { RouteInfoPathExtractor } from './route-info-path-extractor.js'; +import { RoutesMapper } from './routes-mapper.js'; +import { RequestMethod, type NestMiddleware } from '@nestjs/common'; +import { + type MiddlewareConfiguration, + type RouteInfo, + type NestApplicationContextOptions, + isUndefined, +} from '@nestjs/common/internal'; export class MiddlewareModule< TAppOptions extends NestApplicationContextOptions = diff --git a/packages/core/middleware/resolver.ts b/packages/core/middleware/resolver.ts index 99d02f418b3..173487b29fb 100644 --- a/packages/core/middleware/resolver.ts +++ b/packages/core/middleware/resolver.ts @@ -1,8 +1,8 @@ -import { InjectionToken } from '@nestjs/common'; -import { Injector } from '../injector/injector'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; -import { MiddlewareContainer } from './container'; +import type { InjectionToken } from '@nestjs/common'; +import { Injector } from '../injector/injector.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { Module } from '../injector/module.js'; +import { MiddlewareContainer } from './container.js'; export class MiddlewareResolver { constructor( diff --git a/packages/core/middleware/route-info-path-extractor.ts b/packages/core/middleware/route-info-path-extractor.ts index 048d48a5409..e108887aa96 100644 --- a/packages/core/middleware/route-info-path-extractor.ts +++ b/packages/core/middleware/route-info-path-extractor.ts @@ -1,17 +1,15 @@ import { VersioningType } from '@nestjs/common'; +import { ApplicationConfig } from '../application-config.js'; +import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface.js'; +import { isRouteExcluded } from '../router/utils/index.js'; +import { RoutePathFactory } from './../router/route-path-factory.js'; +import type { VersioningOptions } from '@nestjs/common'; import { - RouteInfo, - VersioningOptions, - VersionValue, -} from '@nestjs/common/interfaces'; -import { + type RouteInfo, + type VersionValue, addLeadingSlash, stripEndSlash, -} from '@nestjs/common/utils/shared.utils'; -import { ApplicationConfig } from '../application-config'; -import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface'; -import { isRouteExcluded } from '../router/utils'; -import { RoutePathFactory } from './../router/route-path-factory'; +} from '@nestjs/common/internal'; export class RouteInfoPathExtractor { private readonly routePathFactory: RoutePathFactory; diff --git a/packages/core/middleware/routes-mapper.ts b/packages/core/middleware/routes-mapper.ts index d5155aace63..ee61dd65d1c 100644 --- a/packages/core/middleware/routes-mapper.ts +++ b/packages/core/middleware/routes-mapper.ts @@ -1,25 +1,20 @@ +import { ApplicationConfig } from '../application-config.js'; +import { NestContainer } from '../injector/container.js'; +import { Module } from '../injector/module.js'; +import { MetadataScanner } from '../metadata-scanner.js'; +import { PathsExplorer, RouteDefinition } from '../router/paths-explorer.js'; +import { targetModulesByContainer } from '../router/router-module.js'; import { MODULE_PATH, PATH_METADATA, VERSION_METADATA, -} from '@nestjs/common/constants'; -import { - RouteInfo, - Type, - VERSION_NEUTRAL, - VersionValue, -} from '@nestjs/common/interfaces'; -import { + type RouteInfo, + type VersionValue, addLeadingSlash, isString, isUndefined, -} from '@nestjs/common/utils/shared.utils'; -import { ApplicationConfig } from '../application-config'; -import { NestContainer } from '../injector/container'; -import { Module } from '../injector/module'; -import { MetadataScanner } from '../metadata-scanner'; -import { PathsExplorer, RouteDefinition } from '../router/paths-explorer'; -import { targetModulesByContainer } from '../router/router-module'; +} from '@nestjs/common/internal'; +import { type Type, VERSION_NEUTRAL } from '@nestjs/common'; export class RoutesMapper { private readonly pathsExplorer: PathsExplorer; diff --git a/packages/core/middleware/utils.ts b/packages/core/middleware/utils.ts index ec71b77920f..3c09c2c5ef4 100644 --- a/packages/core/middleware/utils.ts +++ b/packages/core/middleware/utils.ts @@ -1,16 +1,17 @@ import { RequestMethod } from '@nestjs/common'; -import { HttpServer, RouteInfo, Type } from '@nestjs/common/interfaces'; +import { iterate } from 'iterare'; +import { pathToRegexp } from 'path-to-regexp'; +import { uid } from 'uid'; +import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface.js'; +import { LegacyRouteConverter } from '../router/legacy-route-converter.js'; +import { isRouteExcluded } from '../router/utils/index.js'; +import type { HttpServer, Type } from '@nestjs/common'; import { + type RouteInfo, addLeadingSlash, isFunction, isString, -} from '@nestjs/common/utils/shared.utils'; -import { iterate } from 'iterare'; -import { pathToRegexp } from 'path-to-regexp'; -import { uid } from 'uid'; -import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface'; -import { LegacyRouteConverter } from '../router/legacy-route-converter'; -import { isRouteExcluded } from '../router/utils'; +} from '@nestjs/common/internal'; export const mapToExcludeRoute = ( routes: (string | RouteInfo)[], diff --git a/packages/core/nest-application-context.ts b/packages/core/nest-application-context.ts index 2708c1dbcb8..1b9feaa2507 100644 --- a/packages/core/nest-application-context.ts +++ b/packages/core/nest-application-context.ts @@ -1,38 +1,36 @@ import { - INestApplicationContext, + type INestApplicationContext, Logger, - LoggerService, - LogLevel, + type LoggerService, + type LogLevel, ShutdownSignal, } from '@nestjs/common'; -import { - Abstract, - DynamicModule, - GetOrResolveOptions, - SelectOptions, - ShutdownHooksOptions, - Type, -} from '@nestjs/common/interfaces'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; import { iterate } from 'iterare'; -import { MESSAGES } from './constants'; -import { UnknownModuleException } from './errors/exceptions'; -import { createContextId } from './helpers/context-id-factory'; +import { MESSAGES } from './constants.js'; +import { UnknownModuleException } from './errors/exceptions/index.js'; +import { createContextId } from './helpers/context-id-factory.js'; import { callAppShutdownHook, callBeforeAppShutdownHook, callModuleBootstrapHook, callModuleDestroyHook, callModuleInitHook, -} from './hooks'; -import { AbstractInstanceResolver } from './injector/abstract-instance-resolver'; -import { ModuleCompiler } from './injector/compiler'; -import { NestContainer } from './injector/container'; -import { Injector } from './injector/injector'; -import { InstanceLinksHost } from './injector/instance-links-host'; -import { ContextId } from './injector/instance-wrapper'; -import { Module } from './injector/module'; +} from './hooks/index.js'; +import { AbstractInstanceResolver } from './injector/abstract-instance-resolver.js'; +import { ModuleCompiler } from './injector/compiler.js'; +import { NestContainer } from './injector/container.js'; +import { Injector } from './injector/injector.js'; +import { InstanceLinksHost } from './injector/instance-links-host.js'; +import { ContextId } from './injector/instance-wrapper.js'; +import { Module } from './injector/module.js'; +import type { Abstract, DynamicModule, Type } from '@nestjs/common'; +import { + type GetOrResolveOptions, + type SelectOptions, + type ShutdownHooksOptions, + type NestApplicationContextOptions, + isEmptyArray, +} from '@nestjs/common/internal'; /** * @publicApi @@ -254,16 +252,9 @@ export class NestApplicationContext< if (this.isInitialized) { return this; } - /* eslint-disable-next-line no-async-promise-executor */ - this.initializationPromise = new Promise(async (resolve, reject) => { - try { - await this.callInitHook(); - await this.callBootstrapHook(); - resolve(); - } catch (err) { - reject(err); - } - }); + this.initializationPromise = this.callInitHook().then(() => + this.callBootstrapHook(), + ); await this.initializationPromise; this.isInitialized = true; @@ -276,6 +267,7 @@ export class NestApplicationContext< */ public async close(signal?: string): Promise { await this.initializationPromise; + await this.prepareClose(); await this.callDestroyHook(); await this.callBeforeShutdownHook(signal); await this.dispose(); @@ -325,10 +317,8 @@ export class NestApplicationContext< signals: (ShutdownSignal | string)[] = [], options: ShutdownHooksOptions = {}, ): this { - if (isEmpty(signals)) { - signals = Object.keys(ShutdownSignal).map( - (key: string) => ShutdownSignal[key], - ); + if (isEmptyArray(signals)) { + signals = Object.values(ShutdownSignal); } else { // given signals array should be unique because // process shouldn't listen to the same signal more than once. @@ -345,6 +335,12 @@ export class NestApplicationContext< return this; } + protected async prepareClose(): Promise { + // Nest application context has no server + // to signal, therefore just call a noop + return Promise.resolve(); + } + protected async dispose(): Promise { // Nest application context has no server // to dispose, therefore just call a noop @@ -372,6 +368,7 @@ export class NestApplicationContext< } receivedSignal = true; await this.initializationPromise; + await this.prepareClose(); await this.callDestroyHook(); await this.callBeforeShutdownHook(signal); await this.dispose(); diff --git a/packages/core/nest-application.ts b/packages/core/nest-application.ts index f01823a7984..3b63638ea97 100644 --- a/packages/core/nest-application.ts +++ b/packages/core/nest-application.ts @@ -1,52 +1,42 @@ import { - CanActivate, - ExceptionFilter, - HttpServer, - INestApplication, - INestMicroservice, - NestHybridApplicationOptions, - NestInterceptor, - PipeTransform, - VersioningOptions, + type CanActivate, + type ExceptionFilter, + type HttpServer, + type INestApplication, + type INestMicroservice, + type NestHybridApplicationOptions, + type NestInterceptor, + type PipeTransform, + type VersioningOptions, VersioningType, - WebSocketAdapter, + type WebSocketAdapter, } from '@nestjs/common'; +import { iterate } from 'iterare'; +import { platform } from 'os'; +import { AbstractHttpAdapter } from './adapters/index.js'; +import { ApplicationConfig } from './application-config.js'; +import { MESSAGES } from './constants.js'; +import { optionalRequire } from './helpers/optional-require.js'; +import { NestContainer } from './injector/container.js'; +import { Injector } from './injector/injector.js'; +import { GraphInspector } from './inspector/graph-inspector.js'; +import { MiddlewareContainer } from './middleware/container.js'; +import { MiddlewareModule } from './middleware/middleware-module.js'; +import { mapToExcludeRoute } from './middleware/utils.js'; +import { NestApplicationContext } from './nest-application-context.js'; +import { Resolver } from './router/interfaces/resolver.interface.js'; +import { RoutesResolver } from './router/routes-resolver.js'; +import { type NestApplicationOptions, Logger } from '@nestjs/common'; import { - GlobalPrefixOptions, - NestApplicationOptions, -} from '@nestjs/common/interfaces'; -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { + type GlobalPrefixOptions, + loadPackage, + loadPackageCached, + tryLoadPackage, addLeadingSlash, isFunction, isObject, isString, -} from '@nestjs/common/utils/shared.utils'; -import { iterate } from 'iterare'; -import { platform } from 'os'; -import { AbstractHttpAdapter } from './adapters'; -import { ApplicationConfig } from './application-config'; -import { MESSAGES } from './constants'; -import { optionalRequire } from './helpers/optional-require'; -import { NestContainer } from './injector/container'; -import { Injector } from './injector/injector'; -import { GraphInspector } from './inspector/graph-inspector'; -import { MiddlewareContainer } from './middleware/container'; -import { MiddlewareModule } from './middleware/middleware-module'; -import { mapToExcludeRoute } from './middleware/utils'; -import { NestApplicationContext } from './nest-application-context'; -import { Resolver } from './router/interfaces/resolver.interface'; -import { RoutesResolver } from './router/routes-resolver'; - -const { SocketModule } = optionalRequire( - '@nestjs/websockets/socket-module', - () => require('@nestjs/websockets/socket-module'), -); -const { MicroservicesModule } = optionalRequire( - '@nestjs/microservices/microservices-module', - () => require('@nestjs/microservices/microservices-module'), -); +} from '@nestjs/common/internal'; /** * @publicApi @@ -62,9 +52,8 @@ export class NestApplication private readonly middlewareContainer = new MiddlewareContainer( this.container, ); - private readonly microservicesModule = - MicroservicesModule && new MicroservicesModule(); - private readonly socketModule = SocketModule && new SocketModule(); + private microservicesModule: any = null; + private socketModule: any = null; private readonly routesResolver: Resolver; private readonly microservices: any[] = []; private httpServer: any; @@ -94,10 +83,14 @@ export class NestApplication ); } + protected async prepareClose(): Promise { + this.httpAdapter && (await this.httpAdapter.beforeClose?.()); + } + protected async dispose(): Promise { - this.socketModule && (await this.socketModule.close()); - this.microservicesModule && (await this.microservicesModule.close()); - this.httpAdapter && (await this.httpAdapter.close()); + await this.socketModule?.close(); + await this.microservicesModule?.close(); + await this.httpAdapter?.close(); await Promise.all( iterate(this.microservices).map(async microservice => { @@ -178,6 +171,11 @@ export class NestApplication return this; } + // Lazy-load optional modules (ESM-compatible) + await Promise.all([ + this.loadSocketModule(), + this.loadMicroservicesModule(), + ]); this.applyOptions(); await this.httpAdapter?.init?.(); @@ -219,11 +217,7 @@ export class NestApplication microserviceOptions: T, hybridAppOptions: NestHybridApplicationOptions = {}, ): INestMicroservice { - const { NestMicroservice } = loadPackage( - '@nestjs/microservices', - 'NestFactory', - () => require('@nestjs/microservices'), - ); + const { NestMicroservice } = loadPackageCached('@nestjs/microservices'); const { inheritAppConfig } = hybridAppOptions; const applicationConfig = inheritAppConfig ? this.config @@ -450,22 +444,36 @@ export class NestApplication public useStaticAssets(options: any): this; public useStaticAssets(path: string, options?: any): this; public useStaticAssets(pathOrOptions: any, options?: any): this { - this.httpAdapter.useStaticAssets && - this.httpAdapter.useStaticAssets(pathOrOptions, options); + this.httpAdapter.useStaticAssets?.(pathOrOptions, options); return this; } public setBaseViewsDir(path: string | string[]): this { - this.httpAdapter.setBaseViewsDir && this.httpAdapter.setBaseViewsDir(path); + this.httpAdapter.setBaseViewsDir?.(path); return this; } public setViewEngine(engineOrOptions: any): this { - this.httpAdapter.setViewEngine && - this.httpAdapter.setViewEngine(engineOrOptions); + this.httpAdapter.setViewEngine?.(engineOrOptions); return this; } + /** + * Pre-load optional packages so that createNestApplication, + * createNestMicroservice and createHttpAdapter can stay synchronous. + */ + public async preloadLazyPackages(): Promise { + // Best-effort: silently swallow if packages are not installed + await tryLoadPackage( + '@nestjs/platform-express', + () => import('@nestjs/platform-express'), + ); + await tryLoadPackage( + '@nestjs/microservices', + () => import('@nestjs/microservices'), + ); + } + private host(): string | undefined { const address = this.httpServer.address(); if (isString(address)) { @@ -494,4 +502,34 @@ export class NestApplication } return instances; } + + private async loadSocketModule() { + if (!this.socketModule) { + const socketModule = await optionalRequire( + '@nestjs/websockets/socket-module', + () => import('@nestjs/websockets/socket-module.js'), + ); + if (socketModule?.SocketModule) { + this.socketModule = new socketModule.SocketModule(); + } + } + } + + private async loadMicroservicesModule() { + if (!this.microservicesModule) { + const msModule = await optionalRequire( + '@nestjs/microservices/microservices-module', + () => import('@nestjs/microservices/microservices-module.js'), + ); + if (msModule?.MicroservicesModule) { + this.microservicesModule = new msModule.MicroservicesModule(); + // Pre-cache the main barrel so connectMicroservice() can stay synchronous + await loadPackage( + '@nestjs/microservices', + 'NestFactory', + () => import('@nestjs/microservices'), + ); + } + } + } } diff --git a/packages/core/nest-factory.ts b/packages/core/nest-factory.ts index ab62ea0f816..6fbaa1358da 100644 --- a/packages/core/nest-factory.ts +++ b/packages/core/nest-factory.ts @@ -1,4 +1,4 @@ -import { +import type { DynamicModule, ForwardReference, HttpServer, @@ -7,29 +7,34 @@ import { INestMicroservice, Type, } from '@nestjs/common'; -import { NestMicroserviceOptions } from '@nestjs/common/interfaces/microservices/nest-microservice-options.interface'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; -import { ConsoleLogger } from '@nestjs/common/services/console-logger.service'; -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; -import { AbstractHttpAdapter } from './adapters/http-adapter'; -import { ApplicationConfig } from './application-config'; -import { MESSAGES } from './constants'; -import { ExceptionsZone } from './errors/exceptions-zone'; -import { loadAdapter } from './helpers/load-adapter'; -import { rethrow } from './helpers/rethrow'; -import { NestContainer } from './injector/container'; -import { Injector } from './injector/injector'; -import { InstanceLoader } from './injector/instance-loader'; -import { GraphInspector } from './inspector/graph-inspector'; -import { NoopGraphInspector } from './inspector/noop-graph-inspector'; -import { UuidFactory, UuidFactoryMode } from './inspector/uuid-factory'; -import { MetadataScanner } from './metadata-scanner'; -import { NestApplication } from './nest-application'; -import { NestApplicationContext } from './nest-application-context'; -import { DependenciesScanner } from './scanner'; +import { AbstractHttpAdapter } from './adapters/http-adapter.js'; +import { ApplicationConfig } from './application-config.js'; +import { MESSAGES } from './constants.js'; +import { ExceptionsZone } from './errors/exceptions-zone.js'; +import { loadAdapter } from './helpers/load-adapter.js'; +import { rethrow } from './helpers/rethrow.js'; +import { NestContainer } from './injector/container.js'; +import { Injector } from './injector/injector.js'; +import { InstanceLoader } from './injector/instance-loader.js'; +import { GraphInspector } from './inspector/graph-inspector.js'; +import { NoopGraphInspector } from './inspector/noop-graph-inspector.js'; +import { UuidFactory, UuidFactoryMode } from './inspector/uuid-factory.js'; +import { MetadataScanner } from './metadata-scanner.js'; +import { NestApplicationContext } from './nest-application-context.js'; +import { NestApplication } from './nest-application.js'; +import { DependenciesScanner } from './scanner.js'; +import { + type NestMicroserviceOptions, + type NestApplicationContextOptions, + loadPackage, + isFunction, + isNil, +} from '@nestjs/common/internal'; +import { + type NestApplicationOptions, + ConsoleLogger, + Logger, +} from '@nestjs/common'; type IEntryNestModule = | Type @@ -83,7 +88,7 @@ export class NestFactoryStatic { ): Promise { const [httpServer, appOptions] = this.isHttpServer(serverOrOptions!) ? [serverOrOptions, options] - : [this.createHttpAdapter(), serverOrOptions]; + : [await this.createHttpAdapter(), serverOrOptions]; const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig, appOptions); @@ -108,6 +113,7 @@ export class NestFactoryStatic { graphInspector, appOptions, ); + await instance.preloadLazyPackages(); const target = this.createNestInstance(instance); return this.createAdapterProxy(target, httpServer); } @@ -125,10 +131,10 @@ export class NestFactoryStatic { moduleCls: IEntryNestModule, options?: NestMicroserviceOptions & T, ): Promise { - const { NestMicroservice } = loadPackage( + const { NestMicroservice } = await loadPackage( '@nestjs/microservices', 'NestFactory', - () => require('@nestjs/microservices'), + () => import('@nestjs/microservices'), ); const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig, options); @@ -315,11 +321,13 @@ export class NestFactoryStatic { this.autoFlushLogs = autoFlushLogs ?? true; } - private createHttpAdapter(httpServer?: T): AbstractHttpAdapter { - const { ExpressAdapter } = loadAdapter( + private async createHttpAdapter( + httpServer?: T, + ): Promise { + const { ExpressAdapter } = await loadAdapter( '@nestjs/platform-express', 'HTTP', - () => require('@nestjs/platform-express'), + () => import('@nestjs/platform-express'), ); return new ExpressAdapter(httpServer); } diff --git a/packages/core/package.json b/packages/core/package.json index 917543b08c8..82c1ca895fb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -4,6 +4,14 @@ "description": "Nest - modern, fast, powerful node.js web framework (@core)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./internal": "./internal.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "homepage": "https://nestjs.com", "funding": { "type": "opencollective", diff --git a/packages/core/pipes/index.ts b/packages/core/pipes/index.ts index ce7301a1d1b..863c11f2502 100644 --- a/packages/core/pipes/index.ts +++ b/packages/core/pipes/index.ts @@ -1,3 +1,3 @@ -export * from './params-token-factory'; -export * from './pipes-consumer'; -export * from './pipes-context-creator'; +export * from './params-token-factory.js'; +export * from './pipes-consumer.js'; +export * from './pipes-context-creator.js'; diff --git a/packages/core/pipes/params-token-factory.ts b/packages/core/pipes/params-token-factory.ts index a7400f91fe3..2095904716a 100644 --- a/packages/core/pipes/params-token-factory.ts +++ b/packages/core/pipes/params-token-factory.ts @@ -1,5 +1,5 @@ -import { Paramtype } from '@nestjs/common'; -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; +import type { Paramtype } from '@nestjs/common'; +import { RouteParamtypes } from '@nestjs/common/internal'; export class ParamsTokenFactory { public exchangeEnumForString(type: RouteParamtypes): Paramtype { diff --git a/packages/core/pipes/pipes-consumer.ts b/packages/core/pipes/pipes-consumer.ts index e7ff75de479..4e2e413b557 100644 --- a/packages/core/pipes/pipes-consumer.ts +++ b/packages/core/pipes/pipes-consumer.ts @@ -1,30 +1,39 @@ -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; -import { ArgumentMetadata, PipeTransform } from '@nestjs/common/interfaces'; -import { ParamsTokenFactory } from './params-token-factory'; +import { ParamsTokenFactory } from './params-token-factory.js'; +import type { RouteParamtypes } from '@nestjs/common/internal'; +import type { ArgumentMetadata, PipeTransform } from '@nestjs/common'; export class PipesConsumer { private readonly paramsTokenFactory = new ParamsTokenFactory(); public async apply( value: TInput, - { metatype, type, data }: ArgumentMetadata, + metadata: ArgumentMetadata, pipes: PipeTransform[], ) { const token = this.paramsTokenFactory.exchangeEnumForString( - type as any as RouteParamtypes, + metadata.type as any as RouteParamtypes, + ); + return this.applyPipes( + value, + { + metatype: metadata.metatype, + type: token, + data: metadata.data, + schema: metadata.schema, + }, + pipes, ); - return this.applyPipes(value, { metatype, type: token, data }, pipes); } public async applyPipes( value: TInput, - { metatype, type, data }: { metatype: any; type?: any; data?: any }, + { metatype, type, data, schema }: ArgumentMetadata, transforms: PipeTransform[], ) { - return transforms.reduce(async (deferredValue, pipe) => { - const val = await deferredValue; - const result = pipe.transform(val, { metatype, type, data }); - return result; - }, Promise.resolve(value)); + let result: unknown = value; + for (const pipe of transforms) { + result = await pipe.transform(result, { metatype, type, data, schema }); + } + return result; } } diff --git a/packages/core/pipes/pipes-context-creator.ts b/packages/core/pipes/pipes-context-creator.ts index 0ea6568cd83..98f4a16052f 100644 --- a/packages/core/pipes/pipes-context-creator.ts +++ b/packages/core/pipes/pipes-context-creator.ts @@ -1,12 +1,16 @@ -import { PIPES_METADATA } from '@nestjs/common/constants'; -import { Controller, PipeTransform, Type } from '@nestjs/common/interfaces'; -import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils'; import { iterate } from 'iterare'; -import { ApplicationConfig } from '../application-config'; -import { ContextCreator } from '../helpers/context-creator'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; +import { ApplicationConfig } from '../application-config.js'; +import { ContextCreator } from '../helpers/context-creator.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { + PIPES_METADATA, + type Controller, + isEmpty, + isFunction, +} from '@nestjs/common/internal'; +import type { PipeTransform, Type } from '@nestjs/common'; export class PipesContextCreator extends ContextCreator { private moduleContext: string; diff --git a/packages/core/repl/index.ts b/packages/core/repl/index.ts index 8a399bd09a5..ba5d2fa62a0 100644 --- a/packages/core/repl/index.ts +++ b/packages/core/repl/index.ts @@ -1 +1 @@ -export * from './repl'; +export * from './repl.js'; diff --git a/packages/core/repl/native-functions/debug-repl-fn.ts b/packages/core/repl/native-functions/debug-repl-fn.ts index 2f1d4a97d14..8104a876560 100644 --- a/packages/core/repl/native-functions/debug-repl-fn.ts +++ b/packages/core/repl/native-functions/debug-repl-fn.ts @@ -1,8 +1,8 @@ -import type { Type, InjectionToken } from '@nestjs/common'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { ReplFunction } from '../repl-function'; -import type { ModuleDebugEntry } from '../repl-context'; -import type { ReplFnDefinition } from '../repl.interfaces'; +import type { InjectionToken, Type } from '@nestjs/common'; +import type { ModuleDebugEntry } from '../repl-context.js'; +import { ReplFunction } from '../repl-function.js'; +import type { ReplFnDefinition } from '../repl.interfaces.js'; +import { clc } from '@nestjs/common/internal'; export class DebugReplFn extends ReplFunction { public fnDefinition: ReplFnDefinition = { @@ -26,12 +26,9 @@ export class DebugReplFn extends ReplFunction { } this.printCtrlsAndProviders(token, moduleEntry); } else { - Object.keys(this.ctx.debugRegistry).forEach(moduleKey => { - this.printCtrlsAndProviders( - moduleKey, - this.ctx.debugRegistry[moduleKey], - ); - }); + for (const [moduleKey, entry] of Object.entries(this.ctx.debugRegistry)) { + this.printCtrlsAndProviders(moduleKey, entry); + } } this.ctx.writeToStdout('\n'); } diff --git a/packages/core/repl/native-functions/get-repl-fn.ts b/packages/core/repl/native-functions/get-repl-fn.ts index 6de27691896..d9d40898c5e 100644 --- a/packages/core/repl/native-functions/get-repl-fn.ts +++ b/packages/core/repl/native-functions/get-repl-fn.ts @@ -1,6 +1,6 @@ import type { Type } from '@nestjs/common'; -import { ReplFunction } from '../repl-function'; -import type { ReplFnDefinition } from '../repl.interfaces'; +import { ReplFunction } from '../repl-function.js'; +import type { ReplFnDefinition } from '../repl.interfaces.js'; export class GetReplFn extends ReplFunction { public fnDefinition: ReplFnDefinition = { diff --git a/packages/core/repl/native-functions/help-repl-fn.ts b/packages/core/repl/native-functions/help-repl-fn.ts index 913ced7ee91..788f46050e6 100644 --- a/packages/core/repl/native-functions/help-repl-fn.ts +++ b/packages/core/repl/native-functions/help-repl-fn.ts @@ -1,7 +1,7 @@ import { iterate } from 'iterare'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { ReplFunction } from '../repl-function'; -import type { ReplFnDefinition } from '../repl.interfaces'; +import { ReplFunction } from '../repl-function.js'; +import type { ReplFnDefinition } from '../repl.interfaces.js'; +import { clc } from '@nestjs/common/internal'; export class HelpReplFn extends ReplFunction { public fnDefinition: ReplFnDefinition = { diff --git a/packages/core/repl/native-functions/index.ts b/packages/core/repl/native-functions/index.ts index b552bc502f5..4ebfcfcb1ec 100644 --- a/packages/core/repl/native-functions/index.ts +++ b/packages/core/repl/native-functions/index.ts @@ -1,6 +1,6 @@ -export * from './help-repl-fn'; -export * from './get-repl-fn'; -export * from './resolve-repl-fn'; -export * from './select-relp-fn'; -export * from './debug-repl-fn'; -export * from './methods-repl-fn'; +export * from './help-repl-fn.js'; +export * from './get-repl-fn.js'; +export * from './resolve-repl-fn.js'; +export * from './select-relp-fn.js'; +export * from './debug-repl-fn.js'; +export * from './methods-repl-fn.js'; diff --git a/packages/core/repl/native-functions/methods-repl-fn.ts b/packages/core/repl/native-functions/methods-repl-fn.ts index f44e1916ed1..97d000b5435 100644 --- a/packages/core/repl/native-functions/methods-repl-fn.ts +++ b/packages/core/repl/native-functions/methods-repl-fn.ts @@ -1,8 +1,8 @@ import type { Type } from '@nestjs/common'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { MetadataScanner } from '../../metadata-scanner'; -import { ReplFunction } from '../repl-function'; -import type { ReplFnDefinition } from '../repl.interfaces'; +import { MetadataScanner } from '../../metadata-scanner.js'; +import { ReplFunction } from '../repl-function.js'; +import type { ReplFnDefinition } from '../repl.interfaces.js'; +import { clc } from '@nestjs/common/internal'; export class MethodsReplFn extends ReplFunction { public fnDefinition: ReplFnDefinition = { diff --git a/packages/core/repl/native-functions/resolve-repl-fn.ts b/packages/core/repl/native-functions/resolve-repl-fn.ts index fe587682d1d..5ca4ba943cf 100644 --- a/packages/core/repl/native-functions/resolve-repl-fn.ts +++ b/packages/core/repl/native-functions/resolve-repl-fn.ts @@ -1,6 +1,6 @@ import type { Type } from '@nestjs/common'; -import { ReplFunction } from '../repl-function'; -import type { ReplFnDefinition } from '../repl.interfaces'; +import { ReplFunction } from '../repl-function.js'; +import type { ReplFnDefinition } from '../repl.interfaces.js'; export class ResolveReplFn extends ReplFunction { public fnDefinition: ReplFnDefinition = { diff --git a/packages/core/repl/native-functions/select-relp-fn.ts b/packages/core/repl/native-functions/select-relp-fn.ts index f35346cd100..d4679e026bb 100644 --- a/packages/core/repl/native-functions/select-relp-fn.ts +++ b/packages/core/repl/native-functions/select-relp-fn.ts @@ -3,8 +3,8 @@ import type { INestApplicationContext, Type, } from '@nestjs/common'; -import { ReplFunction } from '../repl-function'; -import type { ReplFnDefinition } from '../repl.interfaces'; +import { ReplFunction } from '../repl-function.js'; +import type { ReplFnDefinition } from '../repl.interfaces.js'; export class SelectReplFn extends ReplFunction { public fnDefinition: ReplFnDefinition = { diff --git a/packages/core/repl/repl-context.ts b/packages/core/repl/repl-context.ts index f4a2fcd9162..667df90624f 100644 --- a/packages/core/repl/repl-context.ts +++ b/packages/core/repl/repl-context.ts @@ -1,12 +1,12 @@ import { - INestApplicationContext, - InjectionToken, + type INestApplicationContext, + type InjectionToken, Logger, } from '@nestjs/common'; -import { ApplicationConfig } from '../application-config'; -import { ModuleRef, NestContainer } from '../injector'; -import { InternalCoreModule } from '../injector/internal-core-module/internal-core-module'; -import { Module } from '../injector/module'; +import { ApplicationConfig } from '../application-config.js'; +import { ModuleRef, NestContainer } from '../injector/index.js'; +import { InternalCoreModule } from '../injector/internal-core-module/internal-core-module.js'; +import { Module } from '../injector/module.js'; import { DebugReplFn, GetReplFn, @@ -14,9 +14,9 @@ import { MethodsReplFn, ResolveReplFn, SelectReplFn, -} from './native-functions'; -import { ReplFunction } from './repl-function'; -import type { ReplFunctionClass } from './repl.interfaces'; +} from './native-functions/index.js'; +import { ReplFunction } from './repl-function.js'; +import type { ReplFunctionClass } from './repl.interfaces.js'; type ModuleKey = string; export type ModuleDebugEntry = { diff --git a/packages/core/repl/repl-function.ts b/packages/core/repl/repl-function.ts index aa90972c853..64acdc8ba84 100644 --- a/packages/core/repl/repl-function.ts +++ b/packages/core/repl/repl-function.ts @@ -1,7 +1,7 @@ -import { Logger } from '@nestjs/common'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { ReplContext } from './repl-context'; -import type { ReplFnDefinition } from './repl.interfaces'; +import type { Logger } from '@nestjs/common'; +import { ReplContext } from './repl-context.js'; +import type { ReplFnDefinition } from './repl.interfaces.js'; +import { clc } from '@nestjs/common/internal'; export abstract class ReplFunction< ActionParams extends Array = Array, diff --git a/packages/core/repl/repl-logger.ts b/packages/core/repl/repl-logger.ts index c68744d29ea..28ad23a307f 100644 --- a/packages/core/repl/repl-logger.ts +++ b/packages/core/repl/repl-logger.ts @@ -1,7 +1,7 @@ import { ConsoleLogger } from '@nestjs/common'; -import { NestApplication } from '../nest-application'; -import { RouterExplorer } from '../router/router-explorer'; -import { RoutesResolver } from '../router/routes-resolver'; +import { NestApplication } from '../nest-application.js'; +import { RouterExplorer } from '../router/router-explorer.js'; +import { RoutesResolver } from '../router/routes-resolver.js'; export class ReplLogger extends ConsoleLogger { private static readonly ignoredContexts = [ diff --git a/packages/core/repl/repl.interfaces.ts b/packages/core/repl/repl.interfaces.ts index ad4b8fd646c..dfd7a7e6d57 100644 --- a/packages/core/repl/repl.interfaces.ts +++ b/packages/core/repl/repl.interfaces.ts @@ -1,5 +1,5 @@ -import type { ReplContext } from './repl-context'; -import type { ReplFunction } from './repl-function'; +import type { ReplContext } from './repl-context.js'; +import type { ReplFunction } from './repl-function.js'; export type ReplFnDefinition = { /** Function's name. Note that this should be a valid JavaScript function name. */ diff --git a/packages/core/repl/repl.ts b/packages/core/repl/repl.ts index 3bbc1a93a14..1f93926037a 100644 --- a/packages/core/repl/repl.ts +++ b/packages/core/repl/repl.ts @@ -1,13 +1,12 @@ -import { DynamicModule, Logger, Type } from '@nestjs/common'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { NestFactory } from '../nest-factory'; -import { assignToObject } from './assign-to-object.util'; -import { REPL_INITIALIZED_MESSAGE } from './constants'; -import { ReplContext } from './repl-context'; -import { ReplLogger } from './repl-logger'; -import { defineDefaultCommandsOnRepl } from './repl-native-commands'; - +import { type DynamicModule, Logger, type Type } from '@nestjs/common'; +import { NestFactory } from '../nest-factory.js'; +import { assignToObject } from './assign-to-object.util.js'; +import { REPL_INITIALIZED_MESSAGE } from './constants.js'; +import { ReplContext } from './repl-context.js'; +import { ReplLogger } from './repl-logger.js'; +import { defineDefaultCommandsOnRepl } from './repl-native-commands.js'; import type { ReplOptions } from 'repl'; +import { clc } from '@nestjs/common/internal'; export async function repl( module: Type | DynamicModule, diff --git a/packages/core/router/index.ts b/packages/core/router/index.ts index 5e2cc66bd72..d8e0b425b71 100644 --- a/packages/core/router/index.ts +++ b/packages/core/router/index.ts @@ -1,3 +1,3 @@ -export * from './interfaces'; -export * from './request'; -export { RouterModule } from './router-module'; +export * from './interfaces/index.js'; +export * from './request/index.js'; +export { RouterModule } from './router-module.js'; diff --git a/packages/core/router/interfaces/exceptions-filter.interface.ts b/packages/core/router/interfaces/exceptions-filter.interface.ts index 3063fc64df4..03b5a15278e 100644 --- a/packages/core/router/interfaces/exceptions-filter.interface.ts +++ b/packages/core/router/interfaces/exceptions-filter.interface.ts @@ -1,6 +1,6 @@ -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { ExceptionsHandler } from '../../exceptions/exceptions-handler'; -import { ContextId } from '../../injector/instance-wrapper'; +import { ExceptionsHandler } from '../../exceptions/exceptions-handler.js'; +import { ContextId } from '../../injector/instance-wrapper.js'; +import type { Controller } from '@nestjs/common/internal'; export interface ExceptionsFilter { create( diff --git a/packages/core/router/interfaces/exclude-route-metadata.interface.ts b/packages/core/router/interfaces/exclude-route-metadata.interface.ts index b7f93b60098..86b948d9f3b 100644 --- a/packages/core/router/interfaces/exclude-route-metadata.interface.ts +++ b/packages/core/router/interfaces/exclude-route-metadata.interface.ts @@ -1,4 +1,4 @@ -import { RequestMethod } from '@nestjs/common'; +import type { RequestMethod } from '@nestjs/common'; export interface ExcludeRouteMetadata { /** diff --git a/packages/core/router/interfaces/index.ts b/packages/core/router/interfaces/index.ts index 65ccbce89db..441c9ebe693 100644 --- a/packages/core/router/interfaces/index.ts +++ b/packages/core/router/interfaces/index.ts @@ -1 +1 @@ -export * from './routes.interface'; +export * from './routes.interface.js'; diff --git a/packages/core/router/interfaces/route-params-factory.interface.ts b/packages/core/router/interfaces/route-params-factory.interface.ts index 5420269faf0..55696f0b912 100644 --- a/packages/core/router/interfaces/route-params-factory.interface.ts +++ b/packages/core/router/interfaces/route-params-factory.interface.ts @@ -1,4 +1,4 @@ -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; +import type { RouteParamtypes } from '@nestjs/common/internal'; export interface IRouteParamsFactory { exchangeKeyForValue< diff --git a/packages/core/router/interfaces/route-path-metadata.interface.ts b/packages/core/router/interfaces/route-path-metadata.interface.ts index fb8a769a610..d8c0336b7f8 100644 --- a/packages/core/router/interfaces/route-path-metadata.interface.ts +++ b/packages/core/router/interfaces/route-path-metadata.interface.ts @@ -1,5 +1,5 @@ -import { VersioningOptions } from '@nestjs/common'; -import { VersionValue } from '@nestjs/common/interfaces'; +import type { VersioningOptions } from '@nestjs/common'; +import type { VersionValue } from '@nestjs/common/internal'; export interface RoutePathMetadata { /** diff --git a/packages/core/router/interfaces/routes.interface.ts b/packages/core/router/interfaces/routes.interface.ts index 0193c686900..902578c1b05 100644 --- a/packages/core/router/interfaces/routes.interface.ts +++ b/packages/core/router/interfaces/routes.interface.ts @@ -1,4 +1,4 @@ -import { Type } from '@nestjs/common'; +import type { Type } from '@nestjs/common'; export interface RouteTree { path: string; diff --git a/packages/core/router/paths-explorer.ts b/packages/core/router/paths-explorer.ts index eec6a7471f2..40fde0f343d 100644 --- a/packages/core/router/paths-explorer.ts +++ b/packages/core/router/paths-explorer.ts @@ -1,18 +1,16 @@ +import { MetadataScanner } from '../metadata-scanner.js'; +import { RouterProxyCallback } from './router-proxy.js'; import { METHOD_METADATA, PATH_METADATA, VERSION_METADATA, -} from '@nestjs/common/constants'; -import { RequestMethod } from '@nestjs/common/enums'; -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { VersionValue } from '@nestjs/common/interfaces/version-options.interface'; -import { + type Controller, + type VersionValue, addLeadingSlash, isString, isUndefined, -} from '@nestjs/common/utils/shared.utils'; -import { MetadataScanner } from '../metadata-scanner'; -import { RouterProxyCallback } from './router-proxy'; +} from '@nestjs/common/internal'; +import type { RequestMethod } from '@nestjs/common'; export interface RouteDefinition { path: string[]; diff --git a/packages/core/router/request/index.ts b/packages/core/router/request/index.ts index 9424999aaa2..a795ed31093 100644 --- a/packages/core/router/request/index.ts +++ b/packages/core/router/request/index.ts @@ -1 +1 @@ -export { REQUEST } from './request-constants'; +export { REQUEST } from './request-constants.js'; diff --git a/packages/core/router/request/request-providers.ts b/packages/core/router/request/request-providers.ts index 308f79065e0..18a0fafab5f 100644 --- a/packages/core/router/request/request-providers.ts +++ b/packages/core/router/request/request-providers.ts @@ -1,5 +1,5 @@ -import { Provider, Scope } from '@nestjs/common'; -import { REQUEST } from './request-constants'; +import { type Provider, Scope } from '@nestjs/common'; +import { REQUEST } from './request-constants.js'; const noop = () => {}; export const requestProvider: Provider = { diff --git a/packages/core/router/route-params-factory.ts b/packages/core/router/route-params-factory.ts index 200cb780c0e..3ac619dde73 100644 --- a/packages/core/router/route-params-factory.ts +++ b/packages/core/router/route-params-factory.ts @@ -1,5 +1,5 @@ -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; -import { IRouteParamsFactory } from './interfaces/route-params-factory.interface'; +import { IRouteParamsFactory } from './interfaces/route-params-factory.interface.js'; +import { RouteParamtypes } from '@nestjs/common/internal'; export class RouteParamsFactory implements IRouteParamsFactory { public exchangeKeyForValue< diff --git a/packages/core/router/route-path-factory.ts b/packages/core/router/route-path-factory.ts index 159b2ee4ebc..b9f7f166edd 100644 --- a/packages/core/router/route-path-factory.ts +++ b/packages/core/router/route-path-factory.ts @@ -1,19 +1,19 @@ import { - RequestMethod, + type RequestMethod, VERSION_NEUTRAL, - VersioningOptions, + type VersioningOptions, VersioningType, flatten, } from '@nestjs/common'; -import { VersionValue } from '@nestjs/common/interfaces'; +import { ApplicationConfig } from '../application-config.js'; +import { RoutePathMetadata } from './interfaces/route-path-metadata.interface.js'; +import { isRouteExcluded } from './utils/index.js'; import { + type VersionValue, addLeadingSlash, isUndefined, stripEndSlash, -} from '@nestjs/common/utils/shared.utils'; -import { ApplicationConfig } from '../application-config'; -import { RoutePathMetadata } from './interfaces/route-path-metadata.interface'; -import { isRouteExcluded } from './utils'; +} from '@nestjs/common/internal'; export class RoutePathFactory { constructor(private readonly applicationConfig: ApplicationConfig) {} diff --git a/packages/core/router/router-exception-filters.ts b/packages/core/router/router-exception-filters.ts index 67769fa22da..620cc699c11 100644 --- a/packages/core/router/router-exception-filters.ts +++ b/packages/core/router/router-exception-filters.ts @@ -1,15 +1,17 @@ -import { HttpServer } from '@nestjs/common'; -import { EXCEPTION_FILTERS_METADATA } from '@nestjs/common/constants'; -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; +import type { HttpServer } from '@nestjs/common'; import { iterate } from 'iterare'; -import { ApplicationConfig } from '../application-config'; -import { BaseExceptionFilterContext } from '../exceptions/base-exception-filter-context'; -import { ExceptionsHandler } from '../exceptions/exceptions-handler'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { RouterProxyCallback } from './router-proxy'; +import { ApplicationConfig } from '../application-config.js'; +import { BaseExceptionFilterContext } from '../exceptions/base-exception-filter-context.js'; +import { ExceptionsHandler } from '../exceptions/exceptions-handler.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { RouterProxyCallback } from './router-proxy.js'; +import { + EXCEPTION_FILTERS_METADATA, + type Controller, + isEmptyArray, +} from '@nestjs/common/internal'; export class RouterExceptionFilters extends BaseExceptionFilterContext { constructor( @@ -37,7 +39,7 @@ export class RouterExceptionFilters extends BaseExceptionFilterContext { contextId, inquirerId, ); - if (isEmpty(filters)) { + if (isEmptyArray(filters)) { return exceptionHandler; } exceptionHandler.setCustomFilters(filters.reverse()); diff --git a/packages/core/router/router-execution-context.ts b/packages/core/router/router-execution-context.ts index 9f783f06e7d..df6f8564738 100644 --- a/packages/core/router/router-execution-context.ts +++ b/packages/core/router/router-execution-context.ts @@ -1,51 +1,57 @@ +import type { + ArgumentMetadata, + ContextType, + RouteParamMetadata, +} from '@nestjs/common'; import { - CanActivate, + type CanActivate, ForbiddenException, - HttpServer, - ParamData, - PipeTransform, - RequestMethod, + type HttpServer, + type ParamData, + type PipeTransform, + type RequestMethod, } from '@nestjs/common'; import { + type Controller, CUSTOM_ROUTE_ARGS_METADATA, HEADERS_METADATA, HTTP_CODE_METADATA, + isEmptyArray, + isString, REDIRECT_METADATA, RENDER_METADATA, ROUTE_ARGS_METADATA, + RouteParamtypes, SSE_METADATA, -} from '@nestjs/common/constants'; -import { RouteParamMetadata } from '@nestjs/common/decorators'; -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; -import { ContextType, Controller } from '@nestjs/common/interfaces'; -import { isEmpty, isString } from '@nestjs/common/utils/shared.utils'; +} from '@nestjs/common/internal'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; import { IncomingMessage } from 'http'; import { Observable } from 'rxjs'; import { FORBIDDEN_MESSAGE, GuardsConsumer, GuardsContextCreator, -} from '../guards'; -import { ContextUtils } from '../helpers/context-utils'; -import { ExecutionContextHost } from '../helpers/execution-context-host'; +} from '../guards/index.js'; +import { ContextUtils } from '../helpers/context-utils.js'; +import { ExecutionContextHost } from '../helpers/execution-context-host.js'; import { HandleResponseFn, HandlerMetadata, HandlerMetadataStorage, HandlerResponseBasicFn, -} from '../helpers/handler-metadata-storage'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { InterceptorsConsumer } from '../interceptors/interceptors-consumer'; -import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator'; -import { PipesConsumer } from '../pipes/pipes-consumer'; -import { PipesContextCreator } from '../pipes/pipes-context-creator'; -import { IRouteParamsFactory } from './interfaces/route-params-factory.interface'; +} from '../helpers/handler-metadata-storage.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { InterceptorsConsumer } from '../interceptors/interceptors-consumer.js'; +import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator.js'; +import { PipesConsumer } from '../pipes/pipes-consumer.js'; +import { PipesContextCreator } from '../pipes/pipes-context-creator.js'; +import { IRouteParamsFactory } from './interfaces/route-params-factory.interface.js'; import { CustomHeader, RedirectResponse, RouterResponseController, -} from './router-response-controller'; -import { HeaderStream } from './sse-stream'; +} from './router-response-controller.js'; +import { HeaderStream } from './sse-stream.js'; export interface ParamProperties { index: number; @@ -57,6 +63,7 @@ export interface ParamProperties { res: TResponse, next: Function, ) => any; + schema?: StandardSchemaV1; } export class RouterExecutionContext { @@ -233,12 +240,11 @@ export class RouterExecutionContext { ); const httpCode = this.reflectHttpStatusCode(callback); - const httpStatusCode = httpCode - ? httpCode - : this.responseController.getStatusByMethod(requestMethod); + const httpStatusCode = + httpCode ?? this.responseController.getStatusByMethod(requestMethod); const responseHeaders = this.reflectResponseHeaders(callback); - const hasCustomHeaders = !isEmpty(responseHeaders); + const hasCustomHeaders = !isEmptyArray(responseHeaders); const handlerMetadata: HandlerMetadata = { argsLength, fnHandleResponse, @@ -291,7 +297,7 @@ export class RouterExecutionContext { this.pipesContextCreator.setModuleContext(moduleContext); return keys.map(key => { - const { index, data, pipes: pipesCollection } = metadata[key]; + const { index, data, pipes: pipesCollection, schema } = metadata[key]; const pipes = this.pipesContextCreator.createConcreteContext( pipesCollection, contextId, @@ -306,7 +312,14 @@ export class RouterExecutionContext { data, contextFactory!, ); - return { index, extractValue: customExtractValue, type, data, pipes }; + return { + index, + extractValue: customExtractValue, + type, + data, + pipes, + schema, + }; } const numericType = Number(type); const extractValue = ( @@ -319,25 +332,17 @@ export class RouterExecutionContext { res, next, }); - return { index, extractValue, type: numericType, data, pipes }; + return { index, extractValue, type: numericType, data, pipes, schema }; }); } public async getParamValue( value: T, - { - metatype, - type, - data, - }: { metatype: unknown; type: RouteParamtypes; data: unknown }, + metadata: ArgumentMetadata, pipes: PipeTransform[], ): Promise { - if (!isEmpty(pipes)) { - return this.pipesConsumer.apply( - value, - { metatype, type, data } as any, - pipes, - ); + if (!isEmptyArray(pipes)) { + return this.pipesConsumer.apply(value, metadata, pipes); } return value; } @@ -395,13 +400,14 @@ export class RouterExecutionContext { data, metatype, pipes: paramPipes, + schema, } = param; const value = extractValue(req, res, next); args[index] = this.isPipeable(type) ? await this.getParamValue( value, - { metatype, type, data } as any, + { metatype, type, data, schema } as ArgumentMetadata, pipes.concat(paramPipes), ) : value; diff --git a/packages/core/router/router-explorer.ts b/packages/core/router/router-explorer.ts index c90ae5378c6..c3dad80d3aa 100644 --- a/packages/core/router/router-explorer.ts +++ b/packages/core/router/router-explorer.ts @@ -1,50 +1,53 @@ -import { HttpServer } from '@nestjs/common'; -import { PATH_METADATA } from '@nestjs/common/constants'; -import { RequestMethod, VersioningType } from '@nestjs/common/enums'; -import { InternalServerErrorException } from '@nestjs/common/exceptions'; -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { Type } from '@nestjs/common/interfaces/type.interface'; -import { VersionValue } from '@nestjs/common/interfaces/version-options.interface'; -import { Logger } from '@nestjs/common/services/logger.service'; -import { - addLeadingSlash, - isUndefined, -} from '@nestjs/common/utils/shared.utils'; +import type { HttpServer } from '@nestjs/common'; import { pathToRegexp } from 'path-to-regexp'; -import { ApplicationConfig } from '../application-config'; -import { UnknownRequestMappingException } from '../errors/exceptions/unknown-request-mapping.exception'; -import { GuardsConsumer, GuardsContextCreator } from '../guards'; -import { ContextIdFactory } from '../helpers/context-id-factory'; -import { ExecutionContextHost } from '../helpers/execution-context-host'; +import { ApplicationConfig } from '../application-config.js'; +import { UnknownRequestMappingException } from '../errors/exceptions/unknown-request-mapping.exception.js'; +import { GuardsConsumer, GuardsContextCreator } from '../guards/index.js'; +import { ContextIdFactory } from '../helpers/context-id-factory.js'; +import { ExecutionContextHost } from '../helpers/execution-context-host.js'; import { ROUTE_MAPPED_MESSAGE, VERSIONED_ROUTE_MAPPED_MESSAGE, -} from '../helpers/messages'; -import { RouterMethodFactory } from '../helpers/router-method-factory'; -import { STATIC_CONTEXT } from '../injector/constants'; -import { NestContainer } from '../injector/container'; -import { Injector } from '../injector/injector'; -import { ContextId, InstanceWrapper } from '../injector/instance-wrapper'; -import { Module } from '../injector/module'; -import { GraphInspector } from '../inspector/graph-inspector'; +} from '../helpers/messages.js'; +import { RouterMethodFactory } from '../helpers/router-method-factory.js'; +import { STATIC_CONTEXT } from '../injector/constants.js'; +import { NestContainer } from '../injector/container.js'; +import { Injector } from '../injector/injector.js'; +import { ContextId, InstanceWrapper } from '../injector/instance-wrapper.js'; +import { Module } from '../injector/module.js'; +import { GraphInspector } from '../inspector/graph-inspector.js'; import { Entrypoint, HttpEntrypointMetadata, -} from '../inspector/interfaces/entrypoint.interface'; +} from '../inspector/interfaces/entrypoint.interface.js'; import { InterceptorsConsumer, InterceptorsContextCreator, -} from '../interceptors'; -import { MetadataScanner } from '../metadata-scanner'; -import { PipesConsumer, PipesContextCreator } from '../pipes'; -import { ExceptionsFilter } from './interfaces/exceptions-filter.interface'; -import { RoutePathMetadata } from './interfaces/route-path-metadata.interface'; -import { PathsExplorer } from './paths-explorer'; -import { REQUEST_CONTEXT_ID } from './request/request-constants'; -import { RouteParamsFactory } from './route-params-factory'; -import { RoutePathFactory } from './route-path-factory'; -import { RouterExecutionContext } from './router-execution-context'; -import { RouterProxy, RouterProxyCallback } from './router-proxy'; +} from '../interceptors/index.js'; +import { MetadataScanner } from '../metadata-scanner.js'; +import { PipesConsumer, PipesContextCreator } from '../pipes/index.js'; +import { ExceptionsFilter } from './interfaces/exceptions-filter.interface.js'; +import { RoutePathMetadata } from './interfaces/route-path-metadata.interface.js'; +import { PathsExplorer } from './paths-explorer.js'; +import { REQUEST_CONTEXT_ID } from './request/request-constants.js'; +import { RouteParamsFactory } from './route-params-factory.js'; +import { RoutePathFactory } from './route-path-factory.js'; +import { RouterExecutionContext } from './router-execution-context.js'; +import { RouterProxy, RouterProxyCallback } from './router-proxy.js'; +import { + PATH_METADATA, + type Controller, + type VersionValue, + addLeadingSlash, + isUndefined, +} from '@nestjs/common/internal'; +import { + RequestMethod, + VersioningType, + InternalServerErrorException, + type Type, + Logger, +} from '@nestjs/common'; export interface RouteDefinition { path: string[]; diff --git a/packages/core/router/router-module.ts b/packages/core/router/router-module.ts index 72162dcdbf5..8a94555ab6e 100644 --- a/packages/core/router/router-module.ts +++ b/packages/core/router/router-module.ts @@ -1,10 +1,9 @@ -import { DynamicModule, Inject, Module, Type } from '@nestjs/common'; -import { MODULE_PATH } from '@nestjs/common/constants'; -import { normalizePath } from '@nestjs/common/utils/shared.utils'; -import { Module as ModuleClass } from '../injector/module'; -import { ModulesContainer } from '../injector/modules-container'; -import { Routes, RouteTree } from './interfaces'; -import { flattenRoutePaths } from './utils'; +import { type DynamicModule, Inject, Module, type Type } from '@nestjs/common'; +import { Module as ModuleClass } from '../injector/module.js'; +import { ModulesContainer } from '../injector/modules-container.js'; +import { Routes, RouteTree } from './interfaces/index.js'; +import { flattenRoutePaths } from './utils/index.js'; +import { MODULE_PATH, normalizePath } from '@nestjs/common/internal'; export const ROUTES = Symbol('ROUTES'); diff --git a/packages/core/router/router-proxy.ts b/packages/core/router/router-proxy.ts index bf60595cdf5..2e87c5e9a03 100644 --- a/packages/core/router/router-proxy.ts +++ b/packages/core/router/router-proxy.ts @@ -1,5 +1,5 @@ -import { ExceptionsHandler } from '../exceptions/exceptions-handler'; -import { ExecutionContextHost } from '../helpers/execution-context-host'; +import { ExceptionsHandler } from '../exceptions/exceptions-handler.js'; +import { ExecutionContextHost } from '../helpers/execution-context-host.js'; export type RouterProxyCallback = ( req: TRequest, diff --git a/packages/core/router/router-response-controller.ts b/packages/core/router/router-response-controller.ts index 5c5e48ac6a3..8683dada916 100644 --- a/packages/core/router/router-response-controller.ts +++ b/packages/core/router/router-response-controller.ts @@ -1,11 +1,10 @@ import { - HttpServer, + type HttpServer, HttpStatus, Logger, RequestMethod, - MessageEvent, + type MessageEvent, } from '@nestjs/common'; -import { isObject } from '@nestjs/common/utils/shared.utils'; import { IncomingMessage } from 'http'; import { EMPTY, lastValueFrom, Observable, isObservable } from 'rxjs'; import { catchError, concatMap, map } from 'rxjs/operators'; @@ -13,7 +12,8 @@ import { AdditionalHeaders, WritableHeaderStream, SseStream, -} from './sse-stream'; +} from './sse-stream.js'; +import { isObject } from '@nestjs/common/internal'; export interface CustomHeader { name: string; diff --git a/packages/core/router/routes-resolver.ts b/packages/core/router/routes-resolver.ts index 451b855df96..592cd60c3e9 100644 --- a/packages/core/router/routes-resolver.ts +++ b/packages/core/router/routes-resolver.ts @@ -3,34 +3,30 @@ import { HttpException, NotFoundException, } from '@nestjs/common'; +import { ApplicationConfig } from '../application-config.js'; +import { + CONTROLLER_MAPPING_MESSAGE, + VERSIONED_CONTROLLER_MAPPING_MESSAGE, +} from '../helpers/messages.js'; +import { NestContainer } from '../injector/container.js'; +import { Injector } from '../injector/injector.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { GraphInspector } from '../inspector/graph-inspector.js'; +import { MetadataScanner } from '../metadata-scanner.js'; +import { Resolver } from './interfaces/resolver.interface.js'; +import { RoutePathMetadata } from './interfaces/route-path-metadata.interface.js'; +import { RoutePathFactory } from './route-path-factory.js'; +import { RouterExceptionFilters } from './router-exception-filters.js'; +import { RouterExplorer } from './router-explorer.js'; +import { RouterProxy } from './router-proxy.js'; import { HOST_METADATA, MODULE_PATH, VERSION_METADATA, -} from '@nestjs/common/constants'; -import { - Controller, - HttpServer, - Type, - VersionValue, -} from '@nestjs/common/interfaces'; -import { Logger } from '@nestjs/common/services/logger.service'; -import { ApplicationConfig } from '../application-config'; -import { - CONTROLLER_MAPPING_MESSAGE, - VERSIONED_CONTROLLER_MAPPING_MESSAGE, -} from '../helpers/messages'; -import { NestContainer } from '../injector/container'; -import { Injector } from '../injector/injector'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { GraphInspector } from '../inspector/graph-inspector'; -import { MetadataScanner } from '../metadata-scanner'; -import { Resolver } from './interfaces/resolver.interface'; -import { RoutePathMetadata } from './interfaces/route-path-metadata.interface'; -import { RoutePathFactory } from './route-path-factory'; -import { RouterExceptionFilters } from './router-exception-filters'; -import { RouterExplorer } from './router-explorer'; -import { RouterProxy } from './router-proxy'; + type Controller, + type VersionValue, +} from '@nestjs/common/internal'; +import { type HttpServer, type Type, Logger } from '@nestjs/common'; export class RoutesResolver implements Resolver { private readonly logger = new Logger(RoutesResolver.name, { @@ -152,11 +148,9 @@ export class RoutesResolver implements Resolver { }; const handler = this.routerExceptionsFilter.create({}, callback, undefined); const proxy = this.routerProxy.createProxy(callback, handler); + const prefix = this.applicationConfig.getGlobalPrefix(); applicationRef.setNotFoundHandler && - applicationRef.setNotFoundHandler( - proxy, - this.applicationConfig.getGlobalPrefix(), - ); + applicationRef.setNotFoundHandler(proxy, prefix); } public registerExceptionHandler() { @@ -166,7 +160,7 @@ export class RoutesResolver implements Resolver { res: TResponse, next: Function, ) => { - throw this.mapExternalException(err); + throw this.container.getHttpAdapterRef().mapException(err); }; const handler = this.routerExceptionsFilter.create( {}, @@ -175,36 +169,9 @@ export class RoutesResolver implements Resolver { ); const proxy = this.routerProxy.createExceptionLayerProxy(callback, handler); const applicationRef = this.container.getHttpAdapterRef(); + const prefix = this.applicationConfig.getGlobalPrefix(); applicationRef.setErrorHandler && - applicationRef.setErrorHandler( - proxy, - this.applicationConfig.getGlobalPrefix(), - ); - } - - public mapExternalException(err: any) { - switch (true) { - // SyntaxError is thrown by Express body-parser when given invalid JSON (#422, #430) - // URIError is thrown by Express when given a path parameter with an invalid percentage - // encoding, e.g. '%FF' (#8915) - case err instanceof SyntaxError || err instanceof URIError: - return new BadRequestException(err.message); - case this.isHttpFastifyError(err): - return new HttpException(err.message, err.statusCode); - default: - return err; - } - } - - private isHttpFastifyError( - error: any, - ): error is Error & { statusCode: number } { - // condition based on this code - https://github.com/fastify/fastify-error/blob/d669b150a82968322f9f7be992b2f6b463272de3/index.js#L22 - return ( - error.statusCode !== undefined && - error instanceof Error && - error.name === 'FastifyError' - ); + applicationRef.setErrorHandler(proxy, prefix); } private getModulePathMetadata(metatype: Type): string | undefined { diff --git a/packages/core/router/sse-stream.ts b/packages/core/router/sse-stream.ts index 26b5a252202..77fc80786d0 100644 --- a/packages/core/router/sse-stream.ts +++ b/packages/core/router/sse-stream.ts @@ -1,7 +1,7 @@ -import { MessageEvent } from '@nestjs/common/interfaces'; -import { isObject } from '@nestjs/common/utils/shared.utils'; import { IncomingMessage, OutgoingHttpHeaders } from 'http'; import { Transform } from 'stream'; +import type { MessageEvent } from '@nestjs/common'; +import { isObject } from '@nestjs/common/internal'; function toDataString(data: string | object): string { if (isObject(data)) { diff --git a/packages/core/router/utils/exclude-route.util.ts b/packages/core/router/utils/exclude-route.util.ts index 8670f18f438..3ed6c985278 100644 --- a/packages/core/router/utils/exclude-route.util.ts +++ b/packages/core/router/utils/exclude-route.util.ts @@ -1,6 +1,6 @@ import { RequestMethod } from '@nestjs/common'; -import { addLeadingSlash } from '@nestjs/common/utils/shared.utils'; -import { ExcludeRouteMetadata } from '../interfaces/exclude-route-metadata.interface'; +import { ExcludeRouteMetadata } from '../interfaces/exclude-route-metadata.interface.js'; +import { addLeadingSlash } from '@nestjs/common/internal'; export const isRequestMethodAll = (method: RequestMethod) => { return RequestMethod.ALL === method || (method as number) === -1; diff --git a/packages/core/router/utils/flatten-route-paths.util.ts b/packages/core/router/utils/flatten-route-paths.util.ts index 473e19b8192..ed5bd374e5b 100644 --- a/packages/core/router/utils/flatten-route-paths.util.ts +++ b/packages/core/router/utils/flatten-route-paths.util.ts @@ -1,6 +1,6 @@ -import { Type } from '@nestjs/common'; -import { isString, normalizePath } from '@nestjs/common/utils/shared.utils'; -import { Routes } from '../interfaces/routes.interface'; +import type { Type } from '@nestjs/common'; +import { Routes } from '../interfaces/routes.interface.js'; +import { isString, normalizePath } from '@nestjs/common/internal'; export function flattenRoutePaths(routes: Routes) { const result: Array<{ diff --git a/packages/core/router/utils/index.ts b/packages/core/router/utils/index.ts index a3524ed9063..cb8b40c19f9 100644 --- a/packages/core/router/utils/index.ts +++ b/packages/core/router/utils/index.ts @@ -1,2 +1,2 @@ -export * from './exclude-route.util'; -export * from './flatten-route-paths.util'; +export * from './exclude-route.util.js'; +export * from './flatten-route-paths.util.js'; diff --git a/packages/core/scanner.ts b/packages/core/scanner.ts index e7345092315..d0385b63e40 100644 --- a/packages/core/scanner.ts +++ b/packages/core/scanner.ts @@ -1,61 +1,59 @@ -import { DynamicModule, ForwardReference, Provider } from '@nestjs/common'; +import type { DynamicModule, ForwardReference, Provider } from '@nestjs/common'; +import { iterate } from 'iterare'; +import { ApplicationConfig } from './application-config.js'; +import { + APP_FILTER, + APP_GUARD, + APP_INTERCEPTOR, + APP_PIPE, + ENHANCER_TOKEN_TO_SUBTYPE_MAP, +} from './constants.js'; +import { CircularDependencyException } from './errors/exceptions/circular-dependency.exception.js'; +import { InvalidClassModuleException } from './errors/exceptions/invalid-class-module.exception.js'; +import { InvalidModuleException } from './errors/exceptions/invalid-module.exception.js'; +import { UndefinedModuleException } from './errors/exceptions/undefined-module.exception.js'; +import { getClassScope } from './helpers/get-class-scope.js'; +import { NestContainer } from './injector/container.js'; +import { InstanceWrapper } from './injector/instance-wrapper.js'; +import { InternalCoreModuleFactory } from './injector/internal-core-module/internal-core-module-factory.js'; +import { Module } from './injector/module.js'; +import { TopologyTree } from './injector/topology-tree/topology-tree.js'; +import { GraphInspector } from './inspector/graph-inspector.js'; +import { UuidFactory } from './inspector/uuid-factory.js'; +import { ModuleDefinition } from './interfaces/module-definition.interface.js'; +import { ModuleOverride } from './interfaces/module-override.interface.js'; +import { MetadataScanner } from './metadata-scanner.js'; import { CATCH_WATERMARK, CONTROLLER_WATERMARK, ENHANCER_KEY_TO_SUBTYPE_MAP, EXCEPTION_FILTERS_METADATA, - EnhancerSubtype, + type EnhancerSubtype, GUARDS_METADATA, INJECTABLE_WATERMARK, INTERCEPTORS_METADATA, MODULE_METADATA, PIPES_METADATA, ROUTE_ARGS_METADATA, -} from '@nestjs/common/constants'; -import { - CanActivate, - ClassProvider, - Controller, - ExceptionFilter, - ExistingProvider, - FactoryProvider, - Injectable, - InjectionToken, - NestInterceptor, - PipeTransform, - Scope, - Type, - ValueProvider, -} from '@nestjs/common/interfaces'; -import { + type Controller, + type Injectable, isFunction, isNil, isUndefined, -} from '@nestjs/common/utils/shared.utils'; -import { iterate } from 'iterare'; -import { ApplicationConfig } from './application-config'; +} from '@nestjs/common/internal'; import { - APP_FILTER, - APP_GUARD, - APP_INTERCEPTOR, - APP_PIPE, - ENHANCER_TOKEN_TO_SUBTYPE_MAP, -} from './constants'; -import { CircularDependencyException } from './errors/exceptions/circular-dependency.exception'; -import { InvalidClassModuleException } from './errors/exceptions/invalid-class-module.exception'; -import { InvalidModuleException } from './errors/exceptions/invalid-module.exception'; -import { UndefinedModuleException } from './errors/exceptions/undefined-module.exception'; -import { getClassScope } from './helpers/get-class-scope'; -import { NestContainer } from './injector/container'; -import { InstanceWrapper } from './injector/instance-wrapper'; -import { InternalCoreModuleFactory } from './injector/internal-core-module/internal-core-module-factory'; -import { Module } from './injector/module'; -import { TopologyTree } from './injector/topology-tree/topology-tree'; -import { GraphInspector } from './inspector/graph-inspector'; -import { UuidFactory } from './inspector/uuid-factory'; -import { ModuleDefinition } from './interfaces/module-definition.interface'; -import { ModuleOverride } from './interfaces/module-override.interface'; -import { MetadataScanner } from './metadata-scanner'; + type CanActivate, + type ClassProvider, + type ExceptionFilter, + type ExistingProvider, + type FactoryProvider, + type InjectionToken, + type NestInterceptor, + type PipeTransform, + Scope, + type Type, + type ValueProvider, +} from '@nestjs/common'; interface ApplicationProviderWrapper { moduleKey: string; diff --git a/packages/core/services/index.ts b/packages/core/services/index.ts index b81014ca8e8..5773ced2f59 100644 --- a/packages/core/services/index.ts +++ b/packages/core/services/index.ts @@ -1 +1 @@ -export * from './reflector.service'; +export * from './reflector.service.js'; diff --git a/packages/core/services/reflector.service.ts b/packages/core/services/reflector.service.ts index be666f9a58c..994457f7759 100644 --- a/packages/core/services/reflector.service.ts +++ b/packages/core/services/reflector.service.ts @@ -1,6 +1,6 @@ -import { CustomDecorator, SetMetadata, Type } from '@nestjs/common'; -import { isEmpty, isObject } from '@nestjs/common/utils/shared.utils'; +import { type CustomDecorator, SetMetadata, type Type } from '@nestjs/common'; import { uid } from 'uid'; +import { isEmptyArray, isObject } from '@nestjs/common/internal'; /** * @publicApi @@ -200,7 +200,7 @@ export class Reflector { targets, ).filter(item => item !== undefined); - if (isEmpty(metadataCollection)) { + if (isEmptyArray(metadataCollection)) { return metadataCollection as TResult; } if (metadataCollection.length === 1) { diff --git a/packages/core/test/application-config.spec.ts b/packages/core/test/application-config.spec.ts index 73e23f7e377..a30bc81503a 100644 --- a/packages/core/test/application-config.spec.ts +++ b/packages/core/test/application-config.spec.ts @@ -1,8 +1,7 @@ import { RequestMethod } from '@nestjs/common'; -import { GlobalPrefixOptions } from '@nestjs/common/interfaces'; -import { expect } from 'chai'; -import { ApplicationConfig } from '../application-config'; -import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface'; +import { GlobalPrefixOptions } from '@nestjs/common/interfaces/index.js'; +import { ApplicationConfig } from '../application-config.js'; +import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface.js'; describe('ApplicationConfig', () => { let appConfig: ApplicationConfig; @@ -15,7 +14,7 @@ describe('ApplicationConfig', () => { const path = 'test'; appConfig.setGlobalPrefix(path); - expect(appConfig.getGlobalPrefix()).to.be.eql(path); + expect(appConfig.getGlobalPrefix()).toEqual(path); }); it('should set global path options', () => { const options: GlobalPrefixOptions = { @@ -29,13 +28,13 @@ describe('ApplicationConfig', () => { }; appConfig.setGlobalPrefixOptions(options); - expect(appConfig.getGlobalPrefixOptions()).to.be.eql(options); + expect(appConfig.getGlobalPrefixOptions()).toEqual(options); }); it('should has empty string as a global path by default', () => { - expect(appConfig.getGlobalPrefix()).to.be.eql(''); + expect(appConfig.getGlobalPrefix()).toEqual(''); }); it('should has empty string as a global path option by default', () => { - expect(appConfig.getGlobalPrefixOptions()).to.be.eql({}); + expect(appConfig.getGlobalPrefixOptions()).toEqual({}); }); }); describe('IOAdapter', () => { @@ -43,7 +42,7 @@ describe('ApplicationConfig', () => { const ioAdapter = { test: 0 }; appConfig.setIoAdapter(ioAdapter as any); - expect(appConfig.getIoAdapter()).to.be.eql(ioAdapter); + expect(appConfig.getIoAdapter()).toEqual(ioAdapter); }); }); describe('Pipes', () => { @@ -51,19 +50,19 @@ describe('ApplicationConfig', () => { const pipes = ['test', 'test2']; appConfig.useGlobalPipes(...(pipes as any)); - expect(appConfig.getGlobalPipes()).to.be.eql(pipes); + expect(appConfig.getGlobalPipes()).toEqual(pipes); }); it('should add pipe', () => { const pipe = 'testOne'; appConfig.addGlobalPipe(pipe as any); - expect(appConfig.getGlobalPipes()).to.contain(pipe); + expect(appConfig.getGlobalPipes()).toContain(pipe); }); it('should add global pipe', () => { const pipe = 'testOne'; appConfig.addGlobalRequestPipe(pipe as any); - expect(appConfig.getGlobalRequestPipes()).to.contain(pipe); + expect(appConfig.getGlobalRequestPipes()).toContain(pipe); }); }); describe('Filters', () => { @@ -71,19 +70,19 @@ describe('ApplicationConfig', () => { const filters = ['test', 'test2']; appConfig.useGlobalFilters(...(filters as any)); - expect(appConfig.getGlobalFilters()).to.be.eql(filters); + expect(appConfig.getGlobalFilters()).toEqual(filters); }); it('should add filter', () => { const filter = 'testOne'; appConfig.addGlobalFilter(filter as any); - expect(appConfig.getGlobalFilters()).to.contain(filter); + expect(appConfig.getGlobalFilters()).toContain(filter); }); it('should add request filter', () => { const filter = 'testOne'; appConfig.addGlobalRequestFilter(filter as any); - expect(appConfig.getGlobalRequestFilters()).to.contain(filter); + expect(appConfig.getGlobalRequestFilters()).toContain(filter); }); }); describe('Guards', () => { @@ -91,19 +90,35 @@ describe('ApplicationConfig', () => { const guards = ['test', 'test2']; appConfig.useGlobalGuards(...(guards as any)); - expect(appConfig.getGlobalGuards()).to.be.eql(guards); + expect(appConfig.getGlobalGuards()).toEqual(guards); }); it('should add guard', () => { const guard = 'testOne'; appConfig.addGlobalGuard(guard as any); - expect(appConfig.getGlobalGuards()).to.contain(guard); + expect(appConfig.getGlobalGuards()).toContain(guard); }); it('should add request guard', () => { const guard = 'testOne'; appConfig.addGlobalRequestGuard(guard as any); - expect(appConfig.getGlobalRequestGuards()).to.contain(guard); + expect(appConfig.getGlobalRequestGuards()).toContain(guard); + }); + }); + describe('PreRequestHooks', () => { + it('should set global preRequest hooks', () => { + const hooks = [() => {}, () => {}]; + appConfig.registerPreRequestHook(...(hooks as any)); + + expect(appConfig.getGlobalPreRequestHooks()).toEqual(hooks); + }); + it('should accumulate multiple registerPreRequestHook calls', () => { + const hook1 = () => {}; + const hook2 = () => {}; + appConfig.registerPreRequestHook(hook1 as any); + appConfig.registerPreRequestHook(hook2 as any); + + expect(appConfig.getGlobalPreRequestHooks()).toEqual([hook1, hook2]); }); }); describe('Interceptors', () => { @@ -111,19 +126,19 @@ describe('ApplicationConfig', () => { const interceptors = ['test', 'test2']; appConfig.useGlobalInterceptors(...(interceptors as any)); - expect(appConfig.getGlobalInterceptors()).to.be.eql(interceptors); + expect(appConfig.getGlobalInterceptors()).toEqual(interceptors); }); it('should add interceptor', () => { const interceptor = 'testOne'; appConfig.addGlobalInterceptor(interceptor as any); - expect(appConfig.getGlobalInterceptors()).to.contain(interceptor); + expect(appConfig.getGlobalInterceptors()).toContain(interceptor); }); it('should add request interceptor', () => { const interceptor = 'testOne'; appConfig.addGlobalRequestInterceptor(interceptor as any); - expect(appConfig.getGlobalRequestInterceptors()).to.contain(interceptor); + expect(appConfig.getGlobalRequestInterceptors()).toContain(interceptor); }); }); describe('Versioning', () => { @@ -131,18 +146,70 @@ describe('ApplicationConfig', () => { const options = { type: 'test' }; appConfig.enableVersioning(options as any); - expect(appConfig.getVersioning()).to.be.eql(options); + expect(appConfig.getVersioning()).toEqual(options); }); it('should ignore duplicated versions on defaultVersion array', () => { const options = { type: 'test', defaultVersion: ['1', '2', '2', '1'] }; appConfig.enableVersioning(options as any); - expect(appConfig.getVersioning()!.defaultVersion).to.be.eql(['1', '2']); + expect(appConfig.getVersioning()!.defaultVersion).toEqual(['1', '2']); }); it('should have undefined as the versioning by default', () => { - expect(appConfig.getVersioning()).to.be.eql(undefined); + expect(appConfig.getVersioning()).toEqual(undefined); + }); + + it('should keep scalar defaultVersion unchanged', () => { + const options = { type: 'test', defaultVersion: '1' }; + appConfig.enableVersioning(options as any); + + expect(appConfig.getVersioning()!.defaultVersion).toEqual('1'); + }); + }); + + describe('constructor', () => { + it('should accept an ioAdapter argument', () => { + const ioAdapter = { test: true } as any; + const config = new ApplicationConfig(ioAdapter); + + expect(config.getIoAdapter()).toBe(ioAdapter); + }); + + it('should default ioAdapter to null', () => { + const config = new ApplicationConfig(); + + expect(config.getIoAdapter()).toBeNull(); + }); + }); + + describe('accumulation', () => { + it('should accumulate global pipes via useGlobalPipes and addGlobalPipe', () => { + appConfig.useGlobalPipes('pipe1' as any, 'pipe2' as any); + appConfig.addGlobalPipe('pipe3' as any); + + expect(appConfig.getGlobalPipes()).toEqual(['pipe1', 'pipe2', 'pipe3']); + }); + + it('should accumulate global filters via useGlobalFilters and addGlobalFilter', () => { + appConfig.useGlobalFilters('f1' as any); + appConfig.addGlobalFilter('f2' as any); + + expect(appConfig.getGlobalFilters()).toEqual(['f1', 'f2']); + }); + + it('should accumulate global guards via useGlobalGuards and addGlobalGuard', () => { + appConfig.useGlobalGuards('g1' as any); + appConfig.addGlobalGuard('g2' as any); + + expect(appConfig.getGlobalGuards()).toEqual(['g1', 'g2']); + }); + + it('should accumulate global interceptors via useGlobalInterceptors and addGlobalInterceptor', () => { + appConfig.useGlobalInterceptors('i1' as any); + appConfig.addGlobalInterceptor('i2' as any); + + expect(appConfig.getGlobalInterceptors()).toEqual(['i1', 'i2']); }); }); }); diff --git a/packages/core/test/discovery/discoverable-meta-host-collection.spec.ts b/packages/core/test/discovery/discoverable-meta-host-collection.spec.ts index 078716994b0..bddc8093396 100644 --- a/packages/core/test/discovery/discoverable-meta-host-collection.spec.ts +++ b/packages/core/test/discovery/discoverable-meta-host-collection.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { ModulesContainer } from '../../injector/modules-container'; -import { DiscoverableMetaHostCollection } from '../../discovery/discoverable-meta-host-collection'; +import { DiscoverableMetaHostCollection } from '../../discovery/discoverable-meta-host-collection.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { ModulesContainer } from '../../injector/modules-container.js'; describe('DiscoverableMetaHostCollection', () => { beforeEach(() => { @@ -19,7 +18,7 @@ describe('DiscoverableMetaHostCollection', () => { metadataKey, ); - expect(DiscoverableMetaHostCollection.metaHostLinks.get(TestClass)).to.eq( + expect(DiscoverableMetaHostCollection.metaHostLinks.get(TestClass)).toBe( metadataKey, ); }); @@ -30,7 +29,7 @@ describe('DiscoverableMetaHostCollection', () => { DiscoverableMetaHostCollection.addClassMetaHostLink(TestClass, 'key1'); DiscoverableMetaHostCollection.addClassMetaHostLink(TestClass, 'key2'); - expect(DiscoverableMetaHostCollection.metaHostLinks.get(TestClass)).to.eq( + expect(DiscoverableMetaHostCollection.metaHostLinks.get(TestClass)).toBe( 'key2', ); }); @@ -51,9 +50,9 @@ describe('DiscoverableMetaHostCollection', () => { collection, ); - expect(collection.has(metaKey)).to.be.true; - expect(collection.get(metaKey)!.has(instanceWrapper)).to.be.true; - expect(collection.get(metaKey)!.size).to.eq(1); + expect(collection.has(metaKey)).toBe(true); + expect(collection.get(metaKey)!.has(instanceWrapper)).toBe(true); + expect(collection.get(metaKey)!.size).toBe(1); }); it('should add to existing set when metaKey already exists', () => { @@ -79,9 +78,9 @@ describe('DiscoverableMetaHostCollection', () => { collection, ); - expect(collection.get(metaKey)!.size).to.eq(2); - expect(collection.get(metaKey)!.has(instanceWrapper1)).to.be.true; - expect(collection.get(metaKey)!.has(instanceWrapper2)).to.be.true; + expect(collection.get(metaKey)!.size).toBe(2); + expect(collection.get(metaKey)!.has(instanceWrapper1)).toBe(true); + expect(collection.get(metaKey)!.has(instanceWrapper2)).toBe(true); }); }); @@ -95,8 +94,8 @@ describe('DiscoverableMetaHostCollection', () => { metaKey, ); - expect(result).to.be.instanceOf(Set); - expect(result.size).to.eq(0); + expect(result).toBeInstanceOf(Set); + expect(result.size).toBe(0); }); it('should return empty set when metaKey is not found', () => { @@ -123,8 +122,8 @@ describe('DiscoverableMetaHostCollection', () => { 'non-existent-key', ); - expect(result).to.be.instanceOf(Set); - expect(result.size).to.eq(0); + expect(result).toBeInstanceOf(Set); + expect(result.size).toBe(0); }); it('should return providers with matching metaKey', () => { @@ -148,8 +147,8 @@ describe('DiscoverableMetaHostCollection', () => { metaKey, ); - expect(result.size).to.eq(1); - expect(result.has(instanceWrapper)).to.be.true; + expect(result.size).toBe(1); + expect(result.has(instanceWrapper)).toBe(true); }); }); @@ -163,8 +162,8 @@ describe('DiscoverableMetaHostCollection', () => { metaKey, ); - expect(result).to.be.instanceOf(Set); - expect(result.size).to.eq(0); + expect(result).toBeInstanceOf(Set); + expect(result.size).toBe(0); }); it('should return controllers with matching metaKey', () => { @@ -191,8 +190,8 @@ describe('DiscoverableMetaHostCollection', () => { metaKey, ); - expect(result.size).to.eq(1); - expect(result.has(instanceWrapper)).to.be.true; + expect(result.size).toBe(1); + expect(result.has(instanceWrapper)).toBe(true); }); }); @@ -216,7 +215,7 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, 'any-key', ); - expect(result.size).to.eq(0); + expect(result.size).toBe(0); }); it('should add provider when metaKey is linked', () => { @@ -240,8 +239,8 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, metaKey, ); - expect(result.size).to.eq(1); - expect(result.has(instanceWrapper)).to.be.true; + expect(result.size).toBe(1); + expect(result.has(instanceWrapper)).toBe(true); }); it('should not add provider when metatype is null and inject is not provided (useValue without inject)', () => { @@ -269,7 +268,7 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, metaKey, ); - expect(result.size).to.eq(0); + expect(result.size).toBe(0); }); it('should use instance constructor when metatype is null but inject is provided', () => { @@ -296,7 +295,7 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, metaKey, ); - expect(result.size).to.eq(1); + expect(result.size).toBe(1); }); it('should use instance constructor when inject is provided (useFactory)', () => { @@ -326,7 +325,7 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, metaKey, ); - expect(result.size).to.eq(1); + expect(result.size).toBe(1); }); }); @@ -350,7 +349,7 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, 'any-key', ); - expect(result.size).to.eq(0); + expect(result.size).toBe(0); }); it('should add controller when metaKey is linked', () => { @@ -377,8 +376,8 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, metaKey, ); - expect(result.size).to.eq(1); - expect(result.has(instanceWrapper)).to.be.true; + expect(result.size).toBe(1); + expect(result.has(instanceWrapper)).toBe(true); }); }); @@ -416,9 +415,9 @@ describe('DiscoverableMetaHostCollection', () => { hostContainerRef, metaKey, ); - expect(result.size).to.eq(2); - expect(result.has(instanceWrapper1)).to.be.true; - expect(result.has(instanceWrapper2)).to.be.true; + expect(result.size).toBe(2); + expect(result.has(instanceWrapper1)).toBe(true); + expect(result.has(instanceWrapper2)).toBe(true); }); }); }); diff --git a/packages/core/test/discovery/discovery-service.spec.ts b/packages/core/test/discovery/discovery-service.spec.ts index acdf7ea08e3..2eb3effffb9 100644 --- a/packages/core/test/discovery/discovery-service.spec.ts +++ b/packages/core/test/discovery/discovery-service.spec.ts @@ -1,24 +1,20 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { DiscoveryService } from '../../discovery/discovery-service'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { Module } from '../../injector/module'; -import { ModulesContainer } from '../../injector/modules-container'; -import { DiscoverableMetaHostCollection } from '../../discovery/discoverable-meta-host-collection'; +import { DiscoverableMetaHostCollection } from '../../discovery/discoverable-meta-host-collection.js'; +import { DiscoveryService } from '../../discovery/discovery-service.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { Module } from '../../injector/module.js'; +import { ModulesContainer } from '../../injector/modules-container.js'; describe('DiscoveryService', () => { let discoveryService: DiscoveryService; let modulesContainer: ModulesContainer; - let sandbox: sinon.SinonSandbox; beforeEach(() => { modulesContainer = new ModulesContainer(); discoveryService = new DiscoveryService(modulesContainer); - sandbox = sinon.createSandbox(); }); afterEach(() => { - sandbox.restore(); + vi.restoreAllMocks(); }); describe('createDecorator', () => { @@ -26,9 +22,9 @@ describe('DiscoveryService', () => { const decorator1 = DiscoveryService.createDecorator(); const decorator2 = DiscoveryService.createDecorator(); - expect(decorator1.KEY).to.be.a('string'); - expect(decorator2.KEY).to.be.a('string'); - expect(decorator1.KEY).to.not.equal(decorator2.KEY); + expect(decorator1.KEY).toBeTypeOf('string'); + expect(decorator2.KEY).toBeTypeOf('string'); + expect(decorator1.KEY).not.toBe(decorator2.KEY); }); it('should create a decorator that can decorate classes', () => { @@ -40,7 +36,7 @@ describe('DiscoveryService', () => { class TestClass {} const metadata = Reflect.getMetadata(TestDecorator.KEY, TestClass); - expect(metadata).to.deep.equal({ value: 'test' }); + expect(metadata).toEqual({ value: 'test' }); }); it('should create a decorator that can decorate methods', () => { @@ -57,7 +53,7 @@ describe('DiscoveryService', () => { TestDecorator.KEY, new TestClass().handleClick, ); - expect(metadata).to.deep.equal({ event: 'click' }); + expect(metadata).toEqual({ event: 'click' }); }); it('should use empty object as default metadata when no options provided', () => { @@ -67,11 +63,11 @@ describe('DiscoveryService', () => { class TestClass {} const metadata = Reflect.getMetadata(TestDecorator.KEY, TestClass); - expect(metadata).to.deep.equal({}); + expect(metadata).toEqual({}); }); it('should add class to DiscoverableMetaHostCollection when decorating a class', () => { - const addClassMetaHostLinkSpy = sandbox.spy( + const addClassMetaHostLinkSpy = vi.spyOn( DiscoverableMetaHostCollection, 'addClassMetaHostLink', ); @@ -80,13 +76,15 @@ describe('DiscoveryService', () => { @TestDecorator() class TestClass {} - expect(addClassMetaHostLinkSpy.calledOnce).to.be.true; - expect(addClassMetaHostLinkSpy.calledWith(TestClass, TestDecorator.KEY)) - .to.be.true; + expect(addClassMetaHostLinkSpy).toHaveBeenCalledOnce(); + expect(addClassMetaHostLinkSpy).toHaveBeenCalledWith( + TestClass, + TestDecorator.KEY, + ); }); it('should not add to DiscoverableMetaHostCollection when decorating a method', () => { - const addClassMetaHostLinkSpy = sandbox.spy( + const addClassMetaHostLinkSpy = vi.spyOn( DiscoverableMetaHostCollection, 'addClassMetaHostLink', ); @@ -97,7 +95,7 @@ describe('DiscoveryService', () => { testMethod() {} } - expect(addClassMetaHostLinkSpy.called).to.be.false; + expect(addClassMetaHostLinkSpy).not.toHaveBeenCalled(); }); }); @@ -132,15 +130,15 @@ describe('DiscoveryService', () => { const providers = discoveryService.getProviders(); - expect(providers).to.have.lengthOf(3); - expect(providers).to.include(provider1); - expect(providers).to.include(provider2); - expect(providers).to.include(provider3); + expect(providers).toHaveLength(3); + expect(providers).toContain(provider1); + expect(providers).toContain(provider2); + expect(providers).toContain(provider3); }); it('should return empty array when no modules exist', () => { const providers = discoveryService.getProviders(); - expect(providers).to.be.an('array').that.is.empty; + expect(providers).toHaveLength(0); }); it('should return empty array when modules have no providers', () => { @@ -149,7 +147,7 @@ describe('DiscoveryService', () => { modulesContainer.set('Module1', module1); const providers = discoveryService.getProviders(); - expect(providers).to.be.an('array').that.is.empty; + expect(providers).toHaveLength(0); }); it('should filter providers by metadataKey when provided', () => { @@ -164,31 +162,33 @@ describe('DiscoveryService', () => { }); const providerSet = new Set([provider1, provider2]); - const getProvidersByMetaKeyStub = sandbox - .stub(DiscoverableMetaHostCollection, 'getProvidersByMetaKey') - .returns(providerSet); + const getProvidersByMetaKeyStub = vi + .spyOn(DiscoverableMetaHostCollection, 'getProvidersByMetaKey') + .mockReturnValue(providerSet); const providers = discoveryService.getProviders({ metadataKey }); - expect(getProvidersByMetaKeyStub.calledOnce).to.be.true; - expect( - getProvidersByMetaKeyStub.calledWith(modulesContainer, metadataKey), - ).to.be.true; - expect(providers).to.have.lengthOf(2); - expect(providers).to.include(provider1); - expect(providers).to.include(provider2); + expect(getProvidersByMetaKeyStub).toHaveBeenCalledOnce(); + expect(getProvidersByMetaKeyStub).toHaveBeenCalledWith( + modulesContainer, + metadataKey, + ); + expect(providers).toHaveLength(2); + expect(providers).toContain(provider1); + expect(providers).toContain(provider2); }); it('should return empty array when no providers match the metadataKey', () => { const metadataKey = 'non-existent-key'; const emptySet = new Set(); - sandbox - .stub(DiscoverableMetaHostCollection, 'getProvidersByMetaKey') - .returns(emptySet); + vi.spyOn( + DiscoverableMetaHostCollection, + 'getProvidersByMetaKey', + ).mockReturnValue(emptySet); const providers = discoveryService.getProviders({ metadataKey }); - expect(providers).to.be.an('array').that.is.empty; + expect(providers).toHaveLength(0); }); it('should filter providers by included modules', () => { @@ -230,10 +230,10 @@ describe('DiscoveryService', () => { include: [Module1, Module2], }); - expect(providers).to.have.lengthOf(2); - expect(providers).to.include(provider1); - expect(providers).to.include(provider2); - expect(providers).to.not.include(provider3); + expect(providers).toHaveLength(2); + expect(providers).toContain(provider1); + expect(providers).toContain(provider2); + expect(providers).not.toContain(provider3); }); it('should return empty array when include option is empty array', () => { @@ -246,7 +246,7 @@ describe('DiscoveryService', () => { modulesContainer.set('Module1', module1); const providers = discoveryService.getProviders({ include: [] }); - expect(providers).to.be.an('array').that.is.empty; + expect(providers).toHaveLength(0); }); }); @@ -272,9 +272,9 @@ describe('DiscoveryService', () => { const controllers = discoveryService.getControllers(); - expect(controllers).to.have.lengthOf(2); - expect(controllers).to.include(controller1); - expect(controllers).to.include(controller2); + expect(controllers).toHaveLength(2); + expect(controllers).toContain(controller1); + expect(controllers).toContain(controller2); }); it('should return empty array when no controllers exist', () => { @@ -282,7 +282,7 @@ describe('DiscoveryService', () => { modulesContainer.set('Module1', module1); const controllers = discoveryService.getControllers(); - expect(controllers).to.be.an('array').that.is.empty; + expect(controllers).toHaveLength(0); }); it('should filter controllers by metadataKey when provided', () => { @@ -293,18 +293,19 @@ describe('DiscoveryService', () => { }); const controllerSet = new Set([controller1]); - const getControllersByMetaKeyStub = sandbox - .stub(DiscoverableMetaHostCollection, 'getControllersByMetaKey') - .returns(controllerSet); + const getControllersByMetaKeyStub = vi + .spyOn(DiscoverableMetaHostCollection, 'getControllersByMetaKey') + .mockReturnValue(controllerSet); const controllers = discoveryService.getControllers({ metadataKey }); - expect(getControllersByMetaKeyStub.calledOnce).to.be.true; - expect( - getControllersByMetaKeyStub.calledWith(modulesContainer, metadataKey), - ).to.be.true; - expect(controllers).to.have.lengthOf(1); - expect(controllers).to.include(controller1); + expect(getControllersByMetaKeyStub).toHaveBeenCalledOnce(); + expect(getControllersByMetaKeyStub).toHaveBeenCalledWith( + modulesContainer, + metadataKey, + ); + expect(controllers).toHaveLength(1); + expect(controllers).toContain(controller1); }); it('should filter controllers by included modules', () => { @@ -333,9 +334,9 @@ describe('DiscoveryService', () => { include: [Module1], }); - expect(controllers).to.have.lengthOf(1); - expect(controllers).to.include(controller1); - expect(controllers).to.not.include(controller2); + expect(controllers).toHaveLength(1); + expect(controllers).toContain(controller1); + expect(controllers).not.toContain(controller2); }); }); @@ -361,7 +362,7 @@ describe('DiscoveryService', () => { wrapper, ); - expect(metadata).to.deep.equal({ role: 'admin' }); + expect(metadata).toEqual({ role: 'admin' }); }); it('should retrieve metadata from method using decorator and methodKey', () => { @@ -388,7 +389,7 @@ describe('DiscoveryService', () => { 'onCreate', ); - expect(metadata).to.deep.equal({ event: 'created' }); + expect(metadata).toEqual({ event: 'created' }); }); it('should return undefined when metadata does not exist', () => { @@ -409,7 +410,7 @@ describe('DiscoveryService', () => { wrapper, ); - expect(metadata).to.be.undefined; + expect(metadata).toBeUndefined(); }); it('should return undefined when methodKey does not exist on instance', () => { @@ -433,7 +434,7 @@ describe('DiscoveryService', () => { 'existingMethod', // Use existing method to avoid undefined reference error ); - expect(metadata).to.be.undefined; + expect(metadata).toBeUndefined(); }); it('should use metatype when instance.constructor is undefined', () => { @@ -458,7 +459,7 @@ describe('DiscoveryService', () => { wrapper, ); - expect(metadata).to.deep.equal({ value: 42 }); + expect(metadata).toEqual({ value: 42 }); }); it('should handle undefined instance gracefully', () => { @@ -481,7 +482,7 @@ describe('DiscoveryService', () => { wrapper, ); - expect(metadata).to.deep.equal({ test: 'value' }); + expect(metadata).toEqual({ test: 'value' }); }); }); @@ -495,14 +496,14 @@ describe('DiscoveryService', () => { const modules = (discoveryService as any).getModules(); - expect(modules).to.have.lengthOf(2); - expect(modules).to.include(module1); - expect(modules).to.include(module2); + expect(modules).toHaveLength(2); + expect(modules).toContain(module1); + expect(modules).toContain(module2); }); it('should return empty array when no modules exist', () => { const modules = (discoveryService as any).getModules(); - expect(modules).to.be.an('array').that.is.empty; + expect(modules).toHaveLength(0); }); it('should filter modules by include option', () => { @@ -522,10 +523,10 @@ describe('DiscoveryService', () => { include: [Module1, Module3], }); - expect(modules).to.have.lengthOf(2); - expect(modules).to.include(module1); - expect(modules).to.include(module3); - expect(modules).to.not.include(module2); + expect(modules).toHaveLength(2); + expect(modules).toContain(module1); + expect(modules).toContain(module3); + expect(modules).not.toContain(module2); }); it('should return empty array when include option is empty', () => { @@ -533,7 +534,7 @@ describe('DiscoveryService', () => { modulesContainer.set('Module1', module1); const modules = (discoveryService as any).getModules({ include: [] }); - expect(modules).to.be.an('array').that.is.empty; + expect(modules).toHaveLength(0); }); }); }); diff --git a/packages/core/test/errors/test/exception-handler.spec.ts b/packages/core/test/errors/test/exception-handler.spec.ts index 3fda223a9f6..f54cb7427c5 100644 --- a/packages/core/test/errors/test/exception-handler.spec.ts +++ b/packages/core/test/errors/test/exception-handler.spec.ts @@ -1,7 +1,5 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ExceptionHandler } from '../../../errors/exception-handler'; -import { RuntimeException } from '../../../errors/exceptions/runtime.exception'; +import { ExceptionHandler } from '../../../errors/exception-handler.js'; +import { RuntimeException } from '../../../errors/exceptions/runtime.exception.js'; describe('ExceptionHandler', () => { let instance: ExceptionHandler; @@ -10,18 +8,18 @@ describe('ExceptionHandler', () => { }); describe('handle', () => { let logger: { error: Function }; - let errorSpy: sinon.SinonSpy; + let errorSpy: ReturnType; beforeEach(() => { logger = { error: () => {}, }; (ExceptionHandler as any).logger = logger; - errorSpy = sinon.spy(logger, 'error'); + errorSpy = vi.spyOn(logger, 'error'); }); it('should call the logger.error method with the thrown exception passed as an argument', () => { const exception = new RuntimeException('msg'); instance.handle(exception); - expect(errorSpy.calledWith(exception)).to.be.true; + expect(errorSpy).toHaveBeenCalledWith(exception); }); }); }); diff --git a/packages/core/test/errors/test/exceptions-zone.spec.ts b/packages/core/test/errors/test/exceptions-zone.spec.ts index e5d6c41404d..eaaa78338bf 100644 --- a/packages/core/test/errors/test/exceptions-zone.spec.ts +++ b/packages/core/test/errors/test/exceptions-zone.spec.ts @@ -1,7 +1,5 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; import { Logger } from '@nestjs/common'; -import { ExceptionsZone } from '../../../errors/exceptions-zone'; +import { ExceptionsZone } from '../../../errors/exceptions-zone.js'; describe('ExceptionsZone', () => { const rethrow = err => { @@ -9,27 +7,27 @@ describe('ExceptionsZone', () => { }; describe('run', () => { - let callback: sinon.SinonSpy; + let callback: ReturnType; beforeEach(() => { - callback = sinon.spy(); + callback = vi.fn(); }); it('should call callback', () => { ExceptionsZone.run(callback as any, rethrow, false); - expect(callback.called).to.be.true; + expect(callback).toHaveBeenCalled(); }); describe('when callback throws exception', () => { const exceptionHandler = { handle: () => {}, }; - let handleSpy: sinon.SinonSpy; - let LoggerFlushSpy: sinon.SinonSpy; - before(() => { + let handleSpy: ReturnType; + let LoggerFlushSpy: ReturnType; + beforeAll(() => { (ExceptionsZone as any).exceptionHandler = exceptionHandler; - handleSpy = sinon.spy(exceptionHandler, 'handle'); - LoggerFlushSpy = sinon.spy(Logger, 'flush'); + handleSpy = vi.spyOn(exceptionHandler, 'handle'); + LoggerFlushSpy = vi.spyOn(Logger, 'flush'); }); - after(() => { - LoggerFlushSpy.restore(); + afterAll(() => { + LoggerFlushSpy.mockRestore(); }); describe('when callback throws exception and autoFlushLogs is false', () => { it('should call "handle" method of exceptionHandler and rethrows and not flush logs', () => { @@ -38,11 +36,11 @@ describe('ExceptionsZone', () => { }; expect(() => ExceptionsZone.run(throwsCallback, rethrow, false), - ).to.throws(); + ).toThrow(); - expect(handleSpy.called).to.be.true; + expect(handleSpy).toHaveBeenCalled(); - expect(LoggerFlushSpy.called).to.be.false; + expect(LoggerFlushSpy).not.toHaveBeenCalled(); }); }); @@ -53,50 +51,51 @@ describe('ExceptionsZone', () => { }; expect(() => ExceptionsZone.run(throwsCallback, rethrow, true), - ).to.throws(); + ).toThrow(); - expect(handleSpy.called).to.be.true; + expect(handleSpy).toHaveBeenCalled(); - expect(LoggerFlushSpy.called).to.be.true; + expect(LoggerFlushSpy).toHaveBeenCalled(); }); }); }); }); describe('asyncRun', () => { - let callback: sinon.SinonSpy; + let callback: ReturnType; beforeEach(() => { - callback = sinon.spy(); + callback = vi.fn(); }); it('should call callback', async () => { await ExceptionsZone.asyncRun(callback as any, rethrow, false); - expect(callback.called).to.be.true; + expect(callback).toHaveBeenCalled(); }); describe('when callback throws exception', () => { const exceptionHandler = { handle: () => {}, }; - let handleSpy: sinon.SinonSpy; - let LoggerFlushSpy: sinon.SinonSpy; - before(() => { + let handleSpy: ReturnType; + let LoggerFlushSpy: ReturnType; + beforeAll(() => { (ExceptionsZone as any).exceptionHandler = exceptionHandler; - handleSpy = sinon.spy(exceptionHandler, 'handle'); - LoggerFlushSpy = sinon.spy(Logger, 'flush'); + handleSpy = vi.spyOn(exceptionHandler, 'handle'); + LoggerFlushSpy = vi.spyOn(Logger, 'flush'); }); - after(() => { - LoggerFlushSpy.restore(); + afterAll(() => { + LoggerFlushSpy.mockRestore(); }); describe('when callback throws exception and autoFlushLogs is false', () => { it('should call "handle" method of exceptionHandler and rethrows error and not flush logs', async () => { const throwsCallback = () => { throw new Error(''); }; - expect(ExceptionsZone.asyncRun(throwsCallback, rethrow, false)).to - .eventually.be.rejected; + await expect( + ExceptionsZone.asyncRun(throwsCallback, rethrow, false), + ).rejects.toThrow(); - expect(handleSpy.called).to.be.true; + expect(handleSpy).toHaveBeenCalled(); - expect(LoggerFlushSpy.called).to.be.false; + expect(LoggerFlushSpy).not.toHaveBeenCalled(); }); }); describe('when callback throws exception and autoFlushLogs is true', () => { @@ -104,12 +103,13 @@ describe('ExceptionsZone', () => { const throwsCallback = () => { throw new Error(''); }; - expect(ExceptionsZone.asyncRun(throwsCallback, rethrow, true)).to - .eventually.be.rejected; + await expect( + ExceptionsZone.asyncRun(throwsCallback, rethrow, true), + ).rejects.toThrow(); - expect(handleSpy.called).to.be.true; + expect(handleSpy).toHaveBeenCalled(); - expect(LoggerFlushSpy.called).to.be.true; + expect(LoggerFlushSpy).toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/errors/test/messages.spec.ts b/packages/core/test/errors/test/messages.spec.ts index 1ed76f42266..83c7989849a 100644 --- a/packages/core/test/errors/test/messages.spec.ts +++ b/packages/core/test/errors/test/messages.spec.ts @@ -1,12 +1,11 @@ -import { expect } from 'chai'; -import { UnknownDependenciesException } from '../../../errors/exceptions/unknown-dependencies.exception'; +import { UnknownDependenciesException } from '../../../errors/exceptions/unknown-dependencies.exception.js'; import { INVALID_MODULE_MESSAGE, UNDEFINED_MODULE_MESSAGE, UNKNOWN_EXPORT_MESSAGE, -} from '../../../errors/messages'; -import { Module } from '../../../injector/module'; -import { stringCleaner } from '../../utils/string.cleaner'; +} from '../../../errors/messages.js'; +import { Module } from '../../../injector/module.js'; +import { stringCleaner } from '../../utils/string.cleaner.js'; describe('Error Messages', () => { const CatsModule = { name: 'CatsModule' }; @@ -37,7 +36,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should display the provide token', () => { const expectedResult = @@ -60,7 +59,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should display the provide token as double-quoted string for string-based tokens', () => { const expectedResult = @@ -84,7 +83,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should display the function name', () => { const expectedResult = @@ -107,7 +106,7 @@ describe('Error Messages', () => { dependencies: ['', CatFunction], }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should use "+" if unknown dependency name', () => { const expectedResult = @@ -130,7 +129,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should display the module name', () => { const expectedResult = @@ -166,7 +165,7 @@ describe('Error Messages', () => { ).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should display the symbol name of the provider', () => { const expectedResult = @@ -189,7 +188,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should display the symbol dependency of the provider', () => { const expectedResult = @@ -212,7 +211,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should detect likely import type issue and provide specific guidance', () => { const expectedResult = @@ -238,7 +237,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should detect import type issue with mixed dependencies', () => { const expectedResult = @@ -267,7 +266,7 @@ describe('Error Messages', () => { }).message, ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); it('should add documentation links to export errors', () => { const expectedResult = @@ -283,7 +282,7 @@ describe('Error Messages', () => { UNKNOWN_EXPORT_MESSAGE('TestService', 'TestModule'), ); - expect(actualMessage).to.equal(expectedResult); + expect(actualMessage).toBe(expectedResult); }); }); @@ -303,7 +302,7 @@ Scope [AppModule -> CatsModule]`); UNDEFINED_MODULE_MESSAGE(CatsModule, 0, [AppModule, CatsModule]), ); - expect(actualMessage).to.be.eq(expectedMessage); + expect(actualMessage).toBe(expectedMessage); }); }); @@ -319,7 +318,7 @@ Scope [AppModule -> CatsModule]`); INVALID_MODULE_MESSAGE(CatsModule, 0, [AppModule, CatsModule]), ); - expect(actualMessage).to.be.eq(expectedMessage); + expect(actualMessage).toBe(expectedMessage); }); }); }); diff --git a/packages/core/test/exceptions/base-exception-filter.spec.ts b/packages/core/test/exceptions/base-exception-filter.spec.ts index f348814c919..99f2749feed 100644 --- a/packages/core/test/exceptions/base-exception-filter.spec.ts +++ b/packages/core/test/exceptions/base-exception-filter.spec.ts @@ -1,7 +1,5 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { BaseExceptionFilterContext } from '../../exceptions/base-exception-filter-context'; -import { NestContainer } from '../../injector/container'; +import { BaseExceptionFilterContext } from '../../exceptions/base-exception-filter-context.js'; +import { NestContainer } from '../../injector/container.js'; export class Filter {} @@ -18,7 +16,7 @@ describe('BaseExceptionFilterContext', () => { describe('when param is an object', () => { it('should return instance', () => { const instance = { catch: () => null }; - expect(filter.getFilterInstance(instance)).to.be.eql(instance); + expect(filter.getFilterInstance(instance)).toEqual(instance); }); }); describe('when param is a constructor', () => { @@ -27,14 +25,16 @@ describe('BaseExceptionFilterContext', () => { instance: 'test', getInstanceByContextId: () => wrapper, }; - sinon - .stub(filter, 'getInstanceByMetatype') - .callsFake(() => wrapper as any); - expect(filter.getFilterInstance(Filter)).to.be.eql(wrapper.instance); + vi.spyOn(filter, 'getInstanceByMetatype').mockImplementation( + () => wrapper as any, + ); + expect(filter.getFilterInstance(Filter)).toEqual(wrapper.instance); }); it('should return null', () => { - sinon.stub(filter, 'getInstanceByMetatype').callsFake(() => null!); - expect(filter.getFilterInstance(Filter)).to.be.eql(null); + vi.spyOn(filter, 'getInstanceByMetatype').mockImplementation( + () => null!, + ); + expect(filter.getFilterInstance(Filter)).toEqual(null); }); }); }); @@ -43,7 +43,7 @@ describe('BaseExceptionFilterContext', () => { describe('when "moduleContext" is nil', () => { it('should return undefined', () => { (filter as any).moduleContext = undefined; - expect(filter.getInstanceByMetatype(null!)).to.be.undefined; + expect(filter.getInstanceByMetatype(null!)).toBeUndefined(); }); }); describe('when "moduleContext" is not nil', () => { @@ -53,8 +53,10 @@ describe('BaseExceptionFilterContext', () => { describe('and when module exists', () => { it('should return undefined', () => { - sinon.stub(container.getModules(), 'get').callsFake(() => undefined); - expect(filter.getInstanceByMetatype(null!)).to.be.undefined; + vi.spyOn(container.getModules(), 'get').mockImplementation( + () => undefined, + ); + expect(filter.getInstanceByMetatype(null!)).toBeUndefined(); }); }); @@ -62,10 +64,10 @@ describe('BaseExceptionFilterContext', () => { it('should return instance', () => { const instance = { test: true }; const module = { injectables: { get: () => instance } }; - sinon - .stub(container.getModules(), 'get') - .callsFake(() => module as any); - expect(filter.getInstanceByMetatype(class {})).to.be.eql(instance); + vi.spyOn(container.getModules(), 'get').mockImplementation( + () => module as any, + ); + expect(filter.getInstanceByMetatype(class {})).toEqual(instance); }); }); }); diff --git a/packages/core/test/exceptions/exceptions-handler.spec.ts b/packages/core/test/exceptions/exceptions-handler.spec.ts index 4472a0b36f1..e7b259ec0a4 100644 --- a/packages/core/test/exceptions/exceptions-handler.spec.ts +++ b/packages/core/test/exceptions/exceptions-handler.spec.ts @@ -1,40 +1,38 @@ import { HttpException } from '@nestjs/common'; -import { isNil, isObject } from '@nestjs/common/utils/shared.utils'; -import { expect } from 'chai'; -import * as createHttpError from 'http-errors'; -import * as sinon from 'sinon'; -import { AbstractHttpAdapter } from '../../adapters'; -import { InvalidExceptionFilterException } from '../../errors/exceptions/invalid-exception-filter.exception'; -import { ExceptionsHandler } from '../../exceptions/exceptions-handler'; -import { ExecutionContextHost } from '../../helpers/execution-context-host'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +import { isNil, isObject } from '@nestjs/common/utils/shared.utils.js'; +import createHttpError from 'http-errors'; +import { AbstractHttpAdapter } from '../../adapters/index.js'; +import { InvalidExceptionFilterException } from '../../errors/exceptions/invalid-exception-filter.exception.js'; +import { ExceptionsHandler } from '../../exceptions/exceptions-handler.js'; +import { ExecutionContextHost } from '../../helpers/execution-context-host.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; +import fastifyErrors from '@fastify/error'; describe('ExceptionsHandler', () => { let adapter: AbstractHttpAdapter; let handler: ExceptionsHandler; - let statusStub: sinon.SinonStub; - let jsonStub: sinon.SinonStub; + let statusStub: ReturnType; + let jsonStub: ReturnType; let response: any; beforeEach(() => { adapter = new NoopHttpAdapter({}); handler = new ExceptionsHandler(adapter); - statusStub = sinon.stub(); - jsonStub = sinon.stub(); + statusStub = vi.fn(); + jsonStub = vi.fn(); response = { status: statusStub, json: jsonStub, }; - response.status.returns(response); - response.json.returns(response); + response.status.mockReturnValue(response); + response.json.mockReturnValue(response); }); describe('next', () => { beforeEach(() => { - sinon - .stub(adapter, 'reply') - .callsFake((responseRef: any, body: any, statusCode?: number) => { + vi.spyOn(adapter, 'reply').mockImplementation( + (responseRef: any, body: any, statusCode?: number) => { if (statusCode) { responseRef.status(statusCode); } @@ -44,31 +42,84 @@ describe('ExceptionsHandler', () => { return isObject(body) ? responseRef.json(body) : responseRef.send(String(body)); - }); + }, + ); }); it('should send expected response status code and message when exception is unknown', () => { handler.next(new Error(), new ExecutionContextHost([0, response])); - expect(statusStub.calledWith(500)).to.be.true; - expect( - jsonStub.calledWith({ - statusCode: 500, - message: 'Internal server error', - }), - ).to.be.true; + expect(statusStub).toHaveBeenCalledWith(500); + expect(jsonStub).toHaveBeenCalledWith({ + statusCode: 500, + message: 'Internal server error', + }); + }); + it('should treat fastify errors as http errors', () => { + const fastifyError = fastifyErrors.createError( + 'FST_ERR_CTP_EMPTY_JSON_BODY', + "Body cannot be empty when content-type is set to 'application/json'", + 400, + )(); + handler.next(fastifyError, new ExecutionContextHost([0, response])); + + expect(statusStub).toHaveBeenCalledWith(400); + expect(jsonStub).toHaveBeenCalledWith({ + statusCode: 400, + message: + "Body cannot be empty when content-type is set to 'application/json'", + }); + }); + it('should not treat errors from external API calls as errors from "http-errors" library', () => { + const apiCallError = Object.assign( + new Error('Some external API call failed'), + { status: 400 }, + ); + handler.next(apiCallError, new ExecutionContextHost([0, response])); + + expect(statusStub).toHaveBeenCalledWith(500); + expect(jsonStub).toHaveBeenCalledWith({ + statusCode: 500, + message: 'Internal server error', + }); + }); + it('should treat fastify errors as http errors', () => { + const fastifyError = fastifyErrors.createError( + 'FST_ERR_CTP_EMPTY_JSON_BODY', + "Body cannot be empty when content-type is set to 'application/json'", + 400, + )(); + handler.next(fastifyError, new ExecutionContextHost([0, response])); + + expect(statusStub).toHaveBeenCalledWith(400); + expect(jsonStub).toHaveBeenCalledWith({ + statusCode: 400, + message: + "Body cannot be empty when content-type is set to 'application/json'", + }); + }); + it('should not treat errors from external API calls as errors from "http-errors" library', () => { + const apiCallError = Object.assign( + new Error('Some external API call failed'), + { status: 400 }, + ); + handler.next(apiCallError, new ExecutionContextHost([0, response])); + + expect(statusStub).toHaveBeenCalledWith(500); + expect(jsonStub).toHaveBeenCalledWith({ + statusCode: 500, + message: 'Internal server error', + }); }); describe('when exception is instantiated by "http-errors" library', () => { it('should send expected response status code and message', () => { const error = new createHttpError.NotFound('User does not exist'); handler.next(error, new ExecutionContextHost([0, response])); - expect(statusStub.calledWith(404)).to.be.true; - expect( - jsonStub.calledWith({ - statusCode: 404, - message: 'User does not exist', - }), - ).to.be.true; + expect(statusStub).toHaveBeenCalledWith(404); + expect(jsonStub).toHaveBeenCalledWith({ + statusCode: 404, + message: 'User does not exist', + }); }); }); describe('when exception is an instance of HttpException', () => { @@ -82,8 +133,8 @@ describe('ExceptionsHandler', () => { new ExecutionContextHost([0, response]), ); - expect(statusStub.calledWith(status)).to.be.true; - expect(jsonStub.calledWith(message)).to.be.true; + expect(statusStub).toHaveBeenCalledWith(status); + expect(jsonStub).toHaveBeenCalledWith(message); }); it('should send expected response status code and transform message to json', () => { const status = 401; @@ -94,22 +145,25 @@ describe('ExceptionsHandler', () => { new ExecutionContextHost([0, response]), ); - expect(statusStub.calledWith(status)).to.be.true; - expect(jsonStub.calledWith({ message, statusCode: status })).to.be.true; + expect(statusStub).toHaveBeenCalledWith(status); + expect(jsonStub).toHaveBeenCalledWith({ message, statusCode: status }); }); }); describe('when "invokeCustomFilters" returns true', () => { beforeEach(() => { - sinon.stub(handler, 'invokeCustomFilters').returns(true); + vi.spyOn(handler, 'invokeCustomFilters').mockReturnValue(true); }); it('should do nothing', () => { - handler.next( - new Error(), - sinon.createStubInstance(ExecutionContextHost), - ); + handler.next(new Error(), { + ...Object.fromEntries( + Object.getOwnPropertyNames(ExecutionContextHost.prototype).map( + m => [m, vi.fn()], + ), + ), + } as any); - expect(statusStub.notCalled).to.be.true; - expect(jsonStub.notCalled).to.be.true; + expect(statusStub).not.toHaveBeenCalled(); + expect(jsonStub).not.toHaveBeenCalled(); }); }); }); @@ -117,10 +171,10 @@ describe('ExceptionsHandler', () => { const filters = ['test', 'test2']; it('should set custom filters', () => { handler.setCustomFilters(filters as any); - expect((handler as any).filters).to.be.eql(filters); + expect((handler as any).filters).toEqual(filters); }); it('should throw exception when passed argument is not an array', () => { - expect(() => handler.setCustomFilters(null!)).to.throws( + expect(() => handler.setCustomFilters(null!)).toThrow( InvalidExceptionFilterException, ); }); @@ -128,7 +182,7 @@ describe('ExceptionsHandler', () => { describe('invokeCustomFilters', () => { describe('when filters array is empty', () => { it('should return false', () => { - expect(handler.invokeCustomFilters(null, null!)).to.be.false; + expect(handler.invokeCustomFilters(null, null!)).toBe(false); }); }); describe('when filters array is not empty', () => { @@ -136,7 +190,7 @@ describe('ExceptionsHandler', () => { class TestException {} beforeEach(() => { - funcSpy = sinon.spy(); + funcSpy = vi.fn(); }); describe('when filter exists in filters array', () => { beforeEach(() => { @@ -145,28 +199,30 @@ describe('ExceptionsHandler', () => { }); it('should call funcSpy', () => { handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.false; + expect(funcSpy).toHaveBeenCalled(); }); it('should call funcSpy with exception and response passed as an arguments', () => { const exception = new TestException(); const res = { foo: 'bar' }; handler.invokeCustomFilters(exception, res as any); - expect(funcSpy.calledWith(exception, res)).to.be.true; + expect(funcSpy).toHaveBeenCalledWith(exception, res); }); it('should return true', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .true; + expect(handler.invokeCustomFilters(new TestException(), null!)).toBe( + true, + ); }); }); describe('when filter does not exists in filters array', () => { it('should not call funcSpy', () => { handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.true; + expect(funcSpy).not.toHaveBeenCalled(); }); it('should return false', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .false; + expect(handler.invokeCustomFilters(new TestException(), null!)).toBe( + false, + ); }); }); }); diff --git a/packages/core/test/exceptions/external-exception-filter-context.spec.ts b/packages/core/test/exceptions/external-exception-filter-context.spec.ts index db05ca315e0..0ed821c1b3b 100644 --- a/packages/core/test/exceptions/external-exception-filter-context.spec.ts +++ b/packages/core/test/exceptions/external-exception-filter-context.spec.ts @@ -1,11 +1,9 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Catch } from '../../../common/decorators/core/catch.decorator'; -import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator'; -import { ApplicationConfig } from '../../application-config'; -import { ExternalExceptionFilterContext } from '../../exceptions/external-exception-filter-context'; -import { NestContainer } from '../../injector/container'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; +import { Catch } from '../../../common/decorators/core/catch.decorator.js'; +import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { ExternalExceptionFilterContext } from '../../exceptions/external-exception-filter-context.js'; +import { NestContainer } from '../../injector/container.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; describe('ExternalExceptionFilterContext', () => { let applicationConfig: ApplicationConfig; @@ -31,7 +29,7 @@ describe('ExternalExceptionFilterContext', () => { describe('when filters metadata is empty', () => { class EmptyMetadata {} beforeEach(() => { - sinon.stub(exceptionFilter, 'createContext').returns([]); + vi.spyOn(exceptionFilter, 'createContext').mockReturnValue([]); }); it('should return plain ExceptionHandler object', () => { const filter = exceptionFilter.create( @@ -39,7 +37,7 @@ describe('ExternalExceptionFilterContext', () => { () => ({}) as any, undefined!, ); - expect((filter as any).filters).to.be.empty; + expect((filter as any).filters).toHaveLength(0); }); }); describe('when filters metadata is not empty', () => { @@ -52,7 +50,7 @@ describe('ExternalExceptionFilterContext', () => { () => ({}) as any, undefined!, ); - expect((filter as any).filters).to.not.be.empty; + expect((filter as any).filters).not.toHaveLength(0); }); }); }); @@ -60,12 +58,12 @@ describe('ExternalExceptionFilterContext', () => { it('should return FILTER_CATCH_EXCEPTIONS metadata', () => { expect( exceptionFilter.reflectCatchExceptions(new ExceptionFilter()), - ).to.be.eql([CustomException]); + ).toEqual([CustomException]); }); it('should return an empty array when metadata was found', () => { expect( exceptionFilter.reflectCatchExceptions(new ClassWithNoMetadata()), - ).to.be.eql([]); + ).toEqual([]); }); }); describe('createConcreteContext', () => { @@ -74,11 +72,9 @@ describe('ExternalExceptionFilterContext', () => { it('should return expected exception filters metadata', () => { const resolved = exceptionFilter.createConcreteContext(filters as any); - expect(resolved).to.have.length(1); - expect(resolved[0].exceptionMetatypes).to.be.deep.equal([ - CustomException, - ]); - expect(resolved[0].func).to.be.a('function'); + expect(resolved).toHaveLength(1); + expect(resolved[0].exceptionMetatypes).toEqual([CustomException]); + expect(resolved[0].func).toBeTypeOf('function'); }); }); @@ -86,7 +82,7 @@ describe('ExternalExceptionFilterContext', () => { describe('when contextId is static and inquirerId is nil', () => { it('should return global filters', () => { const expectedResult = applicationConfig.getGlobalFilters(); - expect(exceptionFilter.getGlobalMetadata()).to.be.equal(expectedResult); + expect(exceptionFilter.getGlobalMetadata()).toBe(expectedResult); }); }); describe('otherwise', () => { @@ -96,20 +92,20 @@ describe('ExternalExceptionFilterContext', () => { const instance = 'request-scoped'; const scopedFilterWrappers = [instanceWrapper]; - sinon - .stub(applicationConfig, 'getGlobalFilters') - .callsFake(() => globalFilters); - sinon - .stub(applicationConfig, 'getGlobalRequestFilters') - .callsFake(() => scopedFilterWrappers); - sinon - .stub(instanceWrapper, 'getInstanceByContextId') - .callsFake(() => ({ instance }) as any); - - expect(exceptionFilter.getGlobalMetadata({ id: 3 })).to.contains( - instance, - ...globalFilters, + vi.spyOn(applicationConfig, 'getGlobalFilters').mockImplementation( + () => globalFilters, + ); + vi.spyOn( + applicationConfig, + 'getGlobalRequestFilters', + ).mockImplementation(() => scopedFilterWrappers); + vi.spyOn(instanceWrapper, 'getInstanceByContextId').mockImplementation( + () => ({ instance }) as any, ); + + const result = exceptionFilter.getGlobalMetadata({ id: 3 }); + expect(result).toContain(instance); + globalFilters.forEach(f => expect(result).toContain(f)); }); }); }); diff --git a/packages/core/test/exceptions/external-exceptions-handler.spec.ts b/packages/core/test/exceptions/external-exceptions-handler.spec.ts index cf8ca92a415..3149cba61c9 100644 --- a/packages/core/test/exceptions/external-exceptions-handler.spec.ts +++ b/packages/core/test/exceptions/external-exceptions-handler.spec.ts @@ -1,8 +1,6 @@ -import { expect } from 'chai'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { ExternalExceptionFilter } from '../../exceptions/external-exception-filter'; -import { ExternalExceptionsHandler } from '../../exceptions/external-exceptions-handler'; +import { ExternalExceptionFilter } from '../../exceptions/external-exception-filter.js'; +import { ExternalExceptionsHandler } from '../../exceptions/external-exceptions-handler.js'; describe('ExternalExceptionsHandler', () => { let handler: ExternalExceptionsHandler; @@ -18,16 +16,18 @@ describe('ExternalExceptionsHandler', () => { describe('next', () => { it('should method returns expected stream with message when exception is unknown', () => { const error = new Error(); - expect(() => handler.next(error, null!)).to.throw(error); + expect(() => handler.next(error, null!)).toThrow(error); }); describe('when "invokeCustomFilters" returns value', () => { const observable$ = of(true); beforeEach(() => { - sinon.stub(handler, 'invokeCustomFilters').returns(observable$ as any); + vi.spyOn(handler, 'invokeCustomFilters').mockReturnValue( + observable$ as any, + ); }); it('should return observable', () => { const result = handler.next(new Error(), null!); - expect(result).to.be.eql(observable$); + expect(result).toEqual(observable$); }); }); }); @@ -35,16 +35,16 @@ describe('ExternalExceptionsHandler', () => { const filters = ['test', 'test2']; it('should set custom filters', () => { handler.setCustomFilters(filters as any); - expect((handler as any).filters).to.be.eql(filters); + expect((handler as any).filters).toEqual(filters); }); it('should throw exception when passed argument is not an array', () => { - expect(() => handler.setCustomFilters(null!)).to.throw(); + expect(() => handler.setCustomFilters(null!)).toThrow(); }); }); describe('invokeCustomFilters', () => { describe('when filters array is empty', () => { it('should return identity', () => { - expect(handler.invokeCustomFilters(null, null!)).to.be.null; + expect(handler.invokeCustomFilters(null, null!)).toBeNull(); }); }); describe('when filters array is not empty', () => { @@ -53,7 +53,7 @@ describe('ExternalExceptionsHandler', () => { class AnotherTestException {} beforeEach(() => { - funcSpy = sinon.spy(); + funcSpy = vi.fn(); }); describe('when filter exists in filters array', () => { beforeEach(() => { @@ -62,16 +62,17 @@ describe('ExternalExceptionsHandler', () => { }); it('should call funcSpy', async () => { await handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.false; + expect(funcSpy).toHaveBeenCalled(); }); it('should call funcSpy with exception and response passed as an arguments', async () => { const exception = new TestException(); await handler.invokeCustomFilters(exception, null!); - expect(funcSpy.calledWith(exception)).to.be.true; + expect(funcSpy).toHaveBeenCalledWith(exception, null); }); it('should return stream', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .not.null; + expect( + handler.invokeCustomFilters(new TestException(), null!), + ).not.toBeNull(); }); }); describe('when filter does not exists in filters array', () => { @@ -83,11 +84,12 @@ describe('ExternalExceptionsHandler', () => { }); it('should not call funcSpy', async () => { await handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.true; + expect(funcSpy).not.toHaveBeenCalled(); }); it('should return null', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .null; + expect( + handler.invokeCustomFilters(new TestException(), null!), + ).toBeNull(); }); }); }); diff --git a/packages/core/test/guards/guards-consumer.spec.ts b/packages/core/test/guards/guards-consumer.spec.ts index 516ca0b00a0..f6ddddc5917 100644 --- a/packages/core/test/guards/guards-consumer.spec.ts +++ b/packages/core/test/guards/guards-consumer.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; import { of } from 'rxjs'; -import { GuardsConsumer } from '../../guards/guards-consumer'; +import { GuardsConsumer } from '../../guards/guards-consumer.js'; import { AsyncLocalStorage } from 'async_hooks'; describe('GuardsConsumer', () => { @@ -19,7 +18,7 @@ describe('GuardsConsumer', () => { { constructor: null }, null!, ); - expect(canActivate).to.be.true; + expect(canActivate).toBe(true); }); }); describe('when guards array is not empty', () => { @@ -31,7 +30,7 @@ describe('GuardsConsumer', () => { { constructor: null }, null!, ); - expect(canActivate).to.be.false; + expect(canActivate).toBe(false); }); }); describe('when each guard returns true', () => { @@ -42,7 +41,7 @@ describe('GuardsConsumer', () => { { constructor: null }, null!, ); - expect(canActivate).to.be.true; + expect(canActivate).toBe(true); }); }); describe('when sync guards initialize AsyncLocalStorages', () => { @@ -68,9 +67,9 @@ describe('GuardsConsumer', () => { { constructor: null }, null!, ); - expect(canActivate).to.be.true; - expect(storage1.getStore()).to.equal(1); - expect(storage2.getStore()).to.equal(2); + expect(canActivate).toBe(true); + expect(storage1.getStore()).toBe(1); + expect(storage2.getStore()).toBe(2); }); }); }); @@ -78,12 +77,12 @@ describe('GuardsConsumer', () => { describe('pickResult', () => { describe('when result is Observable', () => { it('should return result', async () => { - expect(await consumer.pickResult(of(true))).to.be.true; + expect(await consumer.pickResult(of(true))).toBe(true); }); }); describe('when result is Promise', () => { it('should await promise', async () => { - expect(await consumer.pickResult(Promise.resolve(true))).to.be.true; + expect(await consumer.pickResult(Promise.resolve(true))).toBe(true); }); }); }); diff --git a/packages/core/test/guards/guards-context-creator.spec.ts b/packages/core/test/guards/guards-context-creator.spec.ts index 0597f4fd45b..6b7ec08a654 100644 --- a/packages/core/test/guards/guards-context-creator.spec.ts +++ b/packages/core/test/guards/guards-context-creator.spec.ts @@ -1,8 +1,6 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ApplicationConfig } from '../../application-config'; -import { GuardsContextCreator } from '../../guards/guards-context-creator'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; +import { ApplicationConfig } from '../../application-config.js'; +import { GuardsContextCreator } from '../../guards/guards-context-creator.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; class Guard {} @@ -11,7 +9,7 @@ describe('GuardsContextCreator', () => { let applicationConfig: ApplicationConfig; let guards: any[]; let container: any; - let getSpy: sinon.SinonSpy; + let getSpy: ReturnType; class Guard1 {} class Guard2 {} @@ -39,7 +37,7 @@ describe('GuardsContextCreator', () => { {}, undefined, ]; - getSpy = sinon.stub().returns({ + getSpy = vi.fn().mockReturnValue({ injectables: new Map([ [Guard1, guards[0]], [Guard2, guards[1]], @@ -60,7 +58,7 @@ describe('GuardsContextCreator', () => { describe('when `moduleContext` is nil', () => { it('should return empty array', () => { const result = guardsContextCreator.createConcreteContext(guards); - expect(result).to.be.empty; + expect(result).toHaveLength(0); }); }); describe('when `moduleContext` is defined', () => { @@ -71,7 +69,7 @@ describe('GuardsContextCreator', () => { const guardTypeRefs = [guards[0].metatype, guards[1].instance]; expect( guardsContextCreator.createConcreteContext(guardTypeRefs), - ).to.have.length(2); + ).toHaveLength(2); }); }); }); @@ -80,7 +78,7 @@ describe('GuardsContextCreator', () => { describe('when param is an object', () => { it('should return instance', () => { const instance = { canActivate: () => null! }; - expect(guardsContextCreator.getGuardInstance(instance)).to.be.eql( + expect(guardsContextCreator.getGuardInstance(instance)).toEqual( instance, ); }); @@ -91,18 +89,20 @@ describe('GuardsContextCreator', () => { instance: 'test', getInstanceByContextId: () => wrapper, }; - sinon - .stub(guardsContextCreator, 'getInstanceByMetatype') - .callsFake(() => wrapper as any); - expect(guardsContextCreator.getGuardInstance(Guard)).to.be.eql( + vi.spyOn( + guardsContextCreator, + 'getInstanceByMetatype', + ).mockImplementation(() => wrapper as any); + expect(guardsContextCreator.getGuardInstance(Guard)).toEqual( wrapper.instance, ); }); it('should return null', () => { - sinon - .stub(guardsContextCreator, 'getInstanceByMetatype') - .callsFake(() => null!); - expect(guardsContextCreator.getGuardInstance(Guard)).to.be.eql(null); + vi.spyOn( + guardsContextCreator, + 'getInstanceByMetatype', + ).mockImplementation(() => null!); + expect(guardsContextCreator.getGuardInstance(Guard)).toEqual(null); }); }); }); @@ -111,8 +111,9 @@ describe('GuardsContextCreator', () => { describe('when "moduleContext" is nil', () => { it('should return undefined', () => { (guardsContextCreator as any).moduleContext = undefined; - expect(guardsContextCreator.getInstanceByMetatype(null!)).to.be - .undefined; + expect( + guardsContextCreator.getInstanceByMetatype(null!), + ).toBeUndefined(); }); }); describe('when "moduleContext" is not nil', () => { @@ -124,7 +125,7 @@ describe('GuardsContextCreator', () => { it('should return undefined', () => { expect( guardsContextCreator.getInstanceByMetatype(class RandomModule {}), - ).to.be.undefined; + ).toBeUndefined(); }); }); }); @@ -134,9 +135,7 @@ describe('GuardsContextCreator', () => { describe('when contextId is static and inquirerId is nil', () => { it('should return global guards', () => { const expectedResult = applicationConfig.getGlobalGuards(); - expect(guardsContextCreator.getGlobalMetadata()).to.be.equal( - expectedResult, - ); + expect(guardsContextCreator.getGlobalMetadata()).toBe(expectedResult); }); }); describe('otherwise', () => { @@ -146,19 +145,19 @@ describe('GuardsContextCreator', () => { const instance = 'request-scoped'; const scopedGuardWrappers = [instanceWrapper]; - sinon - .stub(applicationConfig, 'getGlobalGuards') - .callsFake(() => globalGuards); - sinon - .stub(applicationConfig, 'getGlobalRequestGuards') - .callsFake(() => scopedGuardWrappers); - sinon - .stub(instanceWrapper, 'getInstanceByContextId') - .callsFake(() => ({ instance }) as any); + vi.spyOn(applicationConfig, 'getGlobalGuards').mockImplementation( + () => globalGuards, + ); + vi.spyOn( + applicationConfig, + 'getGlobalRequestGuards', + ).mockImplementation(() => scopedGuardWrappers); + vi.spyOn(instanceWrapper, 'getInstanceByContextId').mockImplementation( + () => ({ instance }) as any, + ); - expect(guardsContextCreator.getGlobalMetadata({ id: 3 })).to.contains( - instance, - ...globalGuards, + expect(guardsContextCreator.getGlobalMetadata({ id: 3 })).toEqual( + expect.arrayContaining([instance, ...globalGuards]), ); }); }); diff --git a/packages/core/test/helpers/application-ref-host.spec.ts b/packages/core/test/helpers/application-ref-host.spec.ts index 78704327e41..d706ea558bf 100644 --- a/packages/core/test/helpers/application-ref-host.spec.ts +++ b/packages/core/test/helpers/application-ref-host.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { HttpAdapterHost } from '../../helpers/http-adapter-host'; +import { HttpAdapterHost } from '../../helpers/http-adapter-host.js'; describe('HttpAdapterHost', () => { let applicationRefHost: HttpAdapterHost; @@ -11,18 +10,19 @@ describe('HttpAdapterHost', () => { const ref = {}; applicationRefHost.httpAdapter = ref as any; - expect(applicationRefHost.httpAdapter).to.be.eql(ref); + expect(applicationRefHost.httpAdapter).toEqual(ref); }); - it('should emit listen event when listening is set to true', done => { - applicationRefHost.listen$.subscribe(() => { - expect(applicationRefHost.listening).to.be.true; - done(); - }); - applicationRefHost.listening = true; - }); + it('should emit listen event when listening is set to true', () => + new Promise(done => { + applicationRefHost.listen$.subscribe(() => { + expect(applicationRefHost.listening).toBe(true); + done(); + }); + applicationRefHost.listening = true; + })); it('listening should return false if the application isnt listening yet', () => { - expect(applicationRefHost.listening).to.be.false; + expect(applicationRefHost.listening).toBe(false); }); }); diff --git a/packages/core/test/helpers/barrier.spec.ts b/packages/core/test/helpers/barrier.spec.ts index e7eefe2a2b2..59c38ed9f10 100644 --- a/packages/core/test/helpers/barrier.spec.ts +++ b/packages/core/test/helpers/barrier.spec.ts @@ -1,19 +1,14 @@ -import { expect } from 'chai'; -import { Barrier } from '../../../core/helpers/barrier'; -import * as sinon from 'sinon'; -import * as chai from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; import { setTimeout } from 'timers/promises'; -chai.use(chaiAsPromised); +import { Barrier } from '../../../core/helpers/barrier.js'; describe('Barrier', () => { const targetCount = 3; let barrier: Barrier; - let barrierResolveSpy: sinon.SinonSpy; + let barrierResolveSpy: ReturnType; beforeEach(() => { barrier = new Barrier(targetCount); - barrierResolveSpy = sinon.spy(barrier, 'resolve'); + barrierResolveSpy = vi.spyOn(barrier, 'resolve'); }); afterEach(() => { @@ -27,7 +22,7 @@ describe('Barrier', () => { barrier.signal(); } - expect(barrierResolveSpy.called).to.be.true; + expect(barrierResolveSpy).toHaveBeenCalled(); }); it('should not resolve the barrier when target count is not reached', async () => { @@ -35,8 +30,8 @@ describe('Barrier', () => { barrier.signal(); } - expect(barrierResolveSpy.called).to.be.false; - expect((barrier).currentCount).to.be.equal(targetCount - 1); + expect(barrierResolveSpy).not.toHaveBeenCalled(); + expect((barrier).currentCount).toBe(targetCount - 1); }); }); @@ -48,17 +43,19 @@ describe('Barrier', () => { barrier.signal(); } - expect(waitPromise).to.be.fulfilled; + await expect(waitPromise).resolves.toBeUndefined(); }); it('should not resolve when target count is not reached', async () => { - const waitPromise = barrier.wait(); - for (let i = 0; i < targetCount - 1; i++) { barrier.signal(); } - expect(waitPromise).not.to.be.fulfilled; + const result = await Promise.race([ + barrier.wait().then(() => 'resolved'), + setTimeout(50).then(() => 'pending'), + ]); + expect(result).toBe('pending'); }); }); @@ -71,23 +68,49 @@ describe('Barrier', () => { // wait for the promise to be resolved await promise; - expect(promise).to.be.fulfilled; - expect(barrierResolveSpy.called).to.be.true; + await expect(promise).resolves.toBeDefined(); + expect(barrierResolveSpy).toHaveBeenCalled(); }); it('should not resolve when target count is not reached', async () => { - const promise = Promise.all( - Array.from({ length: targetCount - 1 }, () => barrier.signalAndWait()), + const promises = Array.from({ length: targetCount - 1 }, () => + barrier.signalAndWait(), ); - /* - * Give the promise some time to work. We cannot await the promise because the test case would - * get stuck. - */ - await setTimeout(5); + const result = await Promise.race([ + Promise.all(promises).then(() => 'resolved'), + setTimeout(50).then(() => 'pending'), + ]); + + expect(result).toBe('pending'); + expect(barrierResolveSpy).not.toHaveBeenCalled(); + }); + }); + + describe('edge cases', () => { + it('should handle over-signaling without errors', () => { + for (let i = 0; i < targetCount + 5; i++) { + expect(() => barrier.signal()).not.toThrow(); + } + }); + + it('should resolve immediately with target count of 1', async () => { + const singleBarrier = new Barrier(1); + const promise = singleBarrier.wait(); + singleBarrier.signal(); + await expect(promise).resolves.toBeUndefined(); + }); + + it('should resolve all waiting consumers', async () => { + const waitPromise1 = barrier.wait(); + const waitPromise2 = barrier.wait(); + + for (let i = 0; i < targetCount; i++) { + barrier.signal(); + } - expect(promise).not.to.be.fulfilled; - expect(barrierResolveSpy.called).to.be.false; + await expect(waitPromise1).resolves.toBeUndefined(); + await expect(waitPromise2).resolves.toBeUndefined(); }); }); }); diff --git a/packages/core/test/helpers/context-id-factory.spec.ts b/packages/core/test/helpers/context-id-factory.spec.ts index 65f0ed82ccd..be78fbc752d 100644 --- a/packages/core/test/helpers/context-id-factory.spec.ts +++ b/packages/core/test/helpers/context-id-factory.spec.ts @@ -1,8 +1,7 @@ -import { expect } from 'chai'; -import { createContextId } from '../../helpers/context-id-factory'; +import { createContextId } from '../../helpers/context-id-factory.js'; describe('createContextId', () => { it('should return an object with random "id" property', () => { - expect(createContextId()).to.have.property('id'); + expect(createContextId()).toHaveProperty('id'); }); }); diff --git a/packages/core/test/helpers/context-utils.spec.ts b/packages/core/test/helpers/context-utils.spec.ts index 0c220f3f415..a5e439e5691 100644 --- a/packages/core/test/helpers/context-utils.spec.ts +++ b/packages/core/test/helpers/context-utils.spec.ts @@ -1,10 +1,13 @@ -import { CUSTOM_ROUTE_ARGS_METADATA } from '@nestjs/common/constants'; -import { Body, createParamDecorator, Request } from '@nestjs/common/decorators'; -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; -import { expect } from 'chai'; -import { ROUTE_ARGS_METADATA } from '../../../common/constants'; -import { ContextUtils } from '../../helpers/context-utils'; -import { ExecutionContextHost } from '../../helpers/execution-context-host'; +import { CUSTOM_ROUTE_ARGS_METADATA } from '@nestjs/common/constants.js'; +import { + Body, + createParamDecorator, + Request, +} from '@nestjs/common/decorators/index.js'; +import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum.js'; +import { ROUTE_ARGS_METADATA } from '../../../common/constants.js'; +import { ContextUtils } from '../../helpers/context-utils.js'; +import { ExecutionContextHost } from '../../helpers/execution-context-host.js'; describe('ContextUtils', () => { let contextUtils: ContextUtils; @@ -46,10 +49,10 @@ describe('ContextUtils', () => { data: undefined, }, }; - expect(metadata[`${RouteParamtypes.REQUEST}:0`]).to.deep.equal( + expect(metadata[`${RouteParamtypes.REQUEST}:0`]).toEqual( expectedMetadata[`${RouteParamtypes.REQUEST}:0`], ); - expect(metadata[`${RouteParamtypes.REQUEST}:1`]).to.deep.equal( + expect(metadata[`${RouteParamtypes.REQUEST}:1`]).toEqual( expectedMetadata[`${RouteParamtypes.REQUEST}:1`], ); @@ -58,10 +61,10 @@ describe('ContextUtils', () => { key.includes(CUSTOM_ROUTE_ARGS_METADATA), )!; - expect(metadata[custom]).to.be.an('object'); - expect(metadata[custom].index).to.be.eq(2); - expect(metadata[custom].data).to.be.eq(undefined); - expect(metadata[custom].factory).to.be.a('function'); + expect(metadata[custom]).toBeTypeOf('object'); + expect(metadata[custom].index).toBe(2); + expect(metadata[custom].data).toBe(undefined); + expect(metadata[custom].factory).toBeTypeOf('function'); }); }); describe('getArgumentsLength', () => { @@ -75,13 +78,13 @@ describe('ContextUtils', () => { }; expect( contextUtils.getArgumentsLength(Object.keys(metadata), metadata), - ).to.be.eq(max + 1); + ).toBe(max + 1); }); }); describe('createNullArray', () => { it('should create N size array filled with null', () => { const size = 3; - expect(contextUtils.createNullArray(size)).to.be.deep.eq([ + expect(contextUtils.createNullArray(size)).toEqual([ undefined, undefined, undefined, @@ -93,7 +96,7 @@ describe('ContextUtils', () => { const paramsProperties = ['1']; expect( contextUtils.mergeParamsMetatypes(paramsProperties as any, null!), - ).to.be.eql(paramsProperties); + ).toEqual(paramsProperties); }); }); describe('getCustomFactory', () => { @@ -107,7 +110,7 @@ describe('ContextUtils', () => { expect( contextUtils.getCustomFactory(customFactory, data, contextFactory)(), - ).to.be.eql(result); + ).toEqual(result); }); }); describe('when factory is undefined / is not a function', () => { @@ -119,8 +122,100 @@ describe('ContextUtils', () => { undefined, contextFactory, )(), - ).to.be.eql(null); + ).toEqual(null); }); }); }); + + describe('mapParamType', () => { + it('should return the type portion before the colon', () => { + expect(contextUtils.mapParamType('body:0')).toBe('body'); + }); + + it('should return the key itself when no colon present', () => { + expect(contextUtils.mapParamType('request')).toBe('request'); + }); + }); + + describe('reflectCallbackParamtypes', () => { + it('should return undefined when no paramtypes metadata exists', () => { + class NoDecorators { + handler() {} + } + const result = contextUtils.reflectCallbackParamtypes( + new NoDecorators(), + 'handler', + ); + expect(result).toBeUndefined(); + }); + }); + + describe('getContextFactory', () => { + it('should return a factory that creates ExecutionContextHost with type set', () => { + class MyController {} + const callback = () => {}; + const factory = contextUtils.getContextFactory( + 'http', + new MyController(), + callback, + ); + const ctx = factory(['arg1', 'arg2']); + expect(ctx).toBeInstanceOf(ExecutionContextHost); + expect(ctx.getType()).toBe('http'); + expect(ctx.getClass()).toBe(MyController); + expect(ctx.getHandler()).toBe(callback); + expect(ctx.getArgs()).toEqual(['arg1', 'arg2']); + }); + + it('should handle undefined instance and callback', () => { + const factory = contextUtils.getContextFactory('rpc'); + const ctx = factory([]); + expect(ctx.getType()).toBe('rpc'); + expect(ctx.getClass()).toBeFalsy(); + }); + }); + + describe('reflectPassthrough', () => { + it('should return undefined when no passthrough metadata exists', () => { + class NoPassthrough { + handler() {} + } + const result = contextUtils.reflectPassthrough( + new NoPassthrough(), + 'handler', + ); + expect(result).toBeUndefined(); + }); + }); + + describe('getArgumentsLength', () => { + it('should return 0 when keys array is empty', () => { + expect(contextUtils.getArgumentsLength([], {})).toBe(0); + }); + }); + + describe('mergeParamsMetatypes', () => { + it('should merge metatype from paramtypes array by index', () => { + const params = [ + { + index: 0, + type: 'body', + data: undefined, + pipes: [], + extractValue: (() => {}) as any, + }, + { + index: 1, + type: 'param', + data: 'id', + pipes: [], + extractValue: (() => {}) as any, + }, + ]; + const paramtypes = [String, Number, Boolean]; + const result = contextUtils.mergeParamsMetatypes(params, paramtypes); + expect(result[0].metatype).toBe(String); + expect(result[1].metatype).toBe(Number); + }); + }); }); diff --git a/packages/core/test/helpers/execution-context-host.spec.ts b/packages/core/test/helpers/execution-context-host.spec.ts index 82f7853bf8f..1a065e6f87f 100644 --- a/packages/core/test/helpers/execution-context-host.spec.ts +++ b/packages/core/test/helpers/execution-context-host.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { ExecutionContextHost } from '../../helpers/execution-context-host'; +import { ExecutionContextHost } from '../../helpers/execution-context-host.js'; describe('ExecutionContextHost', () => { let contextHost: ExecutionContextHost; @@ -18,57 +17,93 @@ describe('ExecutionContextHost', () => { describe('getClass', () => { it('should return constructorRef', () => { - expect(contextHost.getClass()).to.be.eql(constructorRef); + expect(contextHost.getClass()).toEqual(constructorRef); }); }); describe('getHandler', () => { it('should return handler', () => { - expect(contextHost.getHandler()).to.be.eql(callback); + expect(contextHost.getHandler()).toEqual(callback); }); }); describe('getArgs', () => { it('should return args', () => { - expect(contextHost.getArgs()).to.be.eql(args); + expect(contextHost.getArgs()).toEqual(args); }); }); describe('getArgByIndex', () => { it('should return argument by index', () => { - expect(contextHost.getArgByIndex(0)).to.be.eql(args[0]); + expect(contextHost.getArgByIndex(0)).toEqual(args[0]); }); }); describe('switchToRpc', () => { it('should return rpc proxy', () => { const proxy = contextHost.switchToRpc(); - expect(proxy.getData).to.be.a('function'); - expect(proxy.getContext).to.be.a('function'); - expect(proxy.getData()).to.be.eq(args[0]); - expect(proxy.getContext()).to.be.eq(args[1]); + expect(proxy.getData).toBeTypeOf('function'); + expect(proxy.getContext).toBeTypeOf('function'); + expect(proxy.getData()).toBe(args[0]); + expect(proxy.getContext()).toBe(args[1]); }); }); describe('switchToHttp', () => { it('should return http proxy', () => { const proxy = contextHost.switchToHttp(); - expect(proxy.getRequest).to.be.a('function'); - expect(proxy.getResponse).to.be.a('function'); - expect(proxy.getNext).to.be.a('function'); - expect(proxy.getRequest()).to.be.eq(args[0]); - expect(proxy.getResponse()).to.be.eq(args[1]); - expect(proxy.getNext()).to.be.eq(args[2]); + expect(proxy.getRequest).toBeTypeOf('function'); + expect(proxy.getResponse).toBeTypeOf('function'); + expect(proxy.getNext).toBeTypeOf('function'); + expect(proxy.getRequest()).toBe(args[0]); + expect(proxy.getResponse()).toBe(args[1]); + expect(proxy.getNext()).toBe(args[2]); }); }); describe('switchToWs', () => { it('should return ws proxy', () => { const proxy = contextHost.switchToWs(); - expect(proxy.getData).to.be.a('function'); - expect(proxy.getClient).to.be.a('function'); - expect(proxy.getClient()).to.be.eq(args[0]); - expect(proxy.getData()).to.be.eq(args[1]); + expect(proxy.getData).toBeTypeOf('function'); + expect(proxy.getClient).toBeTypeOf('function'); + expect(proxy.getClient()).toBe(args[0]); + expect(proxy.getData()).toBe(args[1]); + }); + + it('should return the last arg via getPattern', () => { + const proxy = contextHost.switchToWs(); + expect(proxy.getPattern()).toBe(args[args.length - 1]); + }); + }); + + describe('getType', () => { + it('should return "http" by default', () => { + expect(contextHost.getType()).toBe('http'); + }); + }); + + describe('setType', () => { + it('should update the context type', () => { + contextHost.setType('ws'); + expect(contextHost.getType()).toBe('ws'); + }); + + it('should not update when type is falsy', () => { + contextHost.setType('' as any); + expect(contextHost.getType()).toBe('http'); + }); + + it('should support custom string types', () => { + contextHost.setType('graphql'); + expect(contextHost.getType()).toBe('graphql'); + }); + }); + + describe('constructor defaults', () => { + it('should allow null constructorRef and handler', () => { + const ctx = new ExecutionContextHost(['a']); + expect(ctx.getClass()).toBeNull(); + expect(ctx.getHandler()).toBeNull(); }); }); }); diff --git a/packages/core/test/helpers/external-context-creator.spec.ts b/packages/core/test/helpers/external-context-creator.spec.ts index 7a87aa4ced9..8c9c0a2e850 100644 --- a/packages/core/test/helpers/external-context-creator.spec.ts +++ b/packages/core/test/helpers/external-context-creator.spec.ts @@ -1,27 +1,25 @@ import { ForbiddenException } from '@nestjs/common'; -import { CUSTOM_ROUTE_ARGS_METADATA } from '@nestjs/common/constants'; -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; -import { expect } from 'chai'; +import { CUSTOM_ROUTE_ARGS_METADATA } from '@nestjs/common/constants.js'; +import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { ExternalExceptionFilterContext } from '../../exceptions/external-exception-filter-context'; -import { GuardsConsumer } from '../../guards/guards-consumer'; -import { GuardsContextCreator } from '../../guards/guards-context-creator'; -import { ExternalContextCreator } from '../../helpers/external-context-creator'; -import { NestContainer } from '../../injector/container'; -import { Module } from '../../injector/module'; -import { ModulesContainer } from '../../injector/modules-container'; -import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer'; -import { InterceptorsContextCreator } from '../../interceptors/interceptors-context-creator'; -import { PipesConsumer } from '../../pipes/pipes-consumer'; -import { PipesContextCreator } from '../../pipes/pipes-context-creator'; -import { RouteParamsFactory } from '../../router/route-params-factory'; +import { ExternalExceptionFilterContext } from '../../exceptions/external-exception-filter-context.js'; +import { GuardsConsumer } from '../../guards/guards-consumer.js'; +import { GuardsContextCreator } from '../../guards/guards-context-creator.js'; +import { ExternalContextCreator } from '../../helpers/external-context-creator.js'; +import { NestContainer } from '../../injector/container.js'; +import { Module } from '../../injector/module.js'; +import { ModulesContainer } from '../../injector/modules-container.js'; +import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer.js'; +import { InterceptorsContextCreator } from '../../interceptors/interceptors-context-creator.js'; +import { PipesConsumer } from '../../pipes/pipes-consumer.js'; +import { PipesContextCreator } from '../../pipes/pipes-context-creator.js'; +import { RouteParamsFactory } from '../../router/route-params-factory.js'; describe('ExternalContextCreator', () => { let contextCreator: ExternalContextCreator; let callback: any; - let bindSpy: sinon.SinonSpy; - let applySpy: sinon.SinonSpy; + let bindSpy: ReturnType; + let applySpy: ReturnType; let guardsConsumer: GuardsConsumer; let pipesConsumer: PipesConsumer; let guardsContextCreator: GuardsContextCreator; @@ -31,13 +29,13 @@ describe('ExternalContextCreator', () => { bind: () => ({}), apply: () => ({}), }; - bindSpy = sinon.spy(callback, 'bind'); - applySpy = sinon.spy(callback, 'apply'); + bindSpy = vi.spyOn(callback, 'bind'); + applySpy = vi.spyOn(callback, 'apply'); guardsConsumer = new GuardsConsumer(); pipesConsumer = new PipesConsumer(); guardsContextCreator = new GuardsContextCreator(new NestContainer()); - sinon.stub(guardsContextCreator, 'create').returns([{}] as any); + vi.spyOn(guardsContextCreator, 'create').mockReturnValue([{}] as any); contextCreator = new ExternalContextCreator( guardsContextCreator, guardsConsumer, @@ -50,15 +48,16 @@ describe('ExternalContextCreator', () => { ); }); describe('create', () => { - it('should call "getContextModuleName" with expected argument', done => { - const getContextModuleKeySpy = sinon.spy( - contextCreator, - 'getContextModuleKey', - ); - contextCreator.create({ foo: 'bar' }, callback, '', '', null!); - expect(getContextModuleKeySpy.called).to.be.true; - done(); - }); + it('should call "getContextModuleName" with expected argument', () => + new Promise(done => { + const getContextModuleKeySpy = vi.spyOn( + contextCreator, + 'getContextModuleKey', + ); + contextCreator.create({ foo: 'bar' }, callback, '', '', null!); + expect(getContextModuleKeySpy).toHaveBeenCalled(); + done(); + })); describe('returns proxy function', () => { let proxyContext; let instance; @@ -68,32 +67,32 @@ describe('ExternalContextCreator', () => { proxyContext = contextCreator.create(instance, callback, '', '', null!); }); it('should be a function', () => { - expect(proxyContext).to.be.a('function'); + expect(proxyContext).toBeTypeOf('function'); }); describe('when proxy function called', () => { describe('when can not activate', () => { it('should throw exception when "tryActivate" returns false', async () => { - sinon - .stub(guardsConsumer, 'tryActivate') - .callsFake(async () => false); + vi.spyOn(guardsConsumer, 'tryActivate').mockImplementation( + async () => false, + ); let err: any; try { await proxyContext(1, 2, 3); } catch (e) { err = e; } - expect(err).to.be.instanceOf(ForbiddenException); + expect(err).toBeInstanceOf(ForbiddenException); }); }); describe('when can activate', () => { it('should apply context and args', async () => { const args = [1, 2, 3]; - sinon - .stub(guardsConsumer, 'tryActivate') - .callsFake(async () => true); + vi.spyOn(guardsConsumer, 'tryActivate').mockImplementation( + async () => true, + ); await proxyContext(...args); - expect(applySpy.called).to.be.true; + expect(applySpy).toHaveBeenCalled(); }); }); }); @@ -102,7 +101,7 @@ describe('ExternalContextCreator', () => { describe('getContextModuleKey', () => { describe('when constructor is undefined', () => { it('should return empty string', () => { - expect(contextCreator.getContextModuleKey(undefined)).to.be.eql(''); + expect(contextCreator.getContextModuleKey(undefined)).toEqual(''); }); }); describe('when module reference provider exists', () => { @@ -114,16 +113,16 @@ describe('ExternalContextCreator', () => { modules.set(moduleKey, moduleRef); (contextCreator as any).modulesContainer = modules; - sinon.stub(moduleRef, 'hasProvider').callsFake(() => true); + vi.spyOn(moduleRef, 'hasProvider').mockImplementation(() => true); expect( contextCreator.getContextModuleKey({ randomObject: true } as any), - ).to.be.eql(moduleKey); + ).toEqual(moduleKey); }); }); describe('when provider does not exists', () => { it('should return empty string', () => { - expect(contextCreator.getContextModuleKey({} as any)).to.be.eql(''); + expect(contextCreator.getContextModuleKey({} as any)).toEqual(''); }); }); }); @@ -150,18 +149,18 @@ describe('ExternalContextCreator', () => { { index: 2, type: RouteParamtypes.BODY, data: 'test' }, { index: 3, type: `key${CUSTOM_ROUTE_ARGS_METADATA}`, data: 'custom' }, ]; - expect(values[0]).to.deep.include(expectedValues[0]); - expect(values[1]).to.deep.include(expectedValues[1]); + expect(values[0]).toMatchObject(expectedValues[0]); + expect(values[1]).toMatchObject(expectedValues[1]); }); }); describe('getParamValue', () => { - let consumerApplySpy: sinon.SinonSpy; + let consumerApplySpy: ReturnType; const value = 3, metatype = null, - transforms = [{ transform: sinon.spy() }]; + transforms = [{ transform: vi.fn() }]; beforeEach(() => { - consumerApplySpy = sinon.spy(pipesConsumer, 'apply'); + consumerApplySpy = vi.spyOn(pipesConsumer, 'apply'); }); it('should call "consumer.apply"', async () => { await contextCreator.getParamValue( @@ -169,14 +168,14 @@ describe('ExternalContextCreator', () => { { metatype, type: RouteParamtypes.NEXT, data: null }, transforms, ); - expect(consumerApplySpy.called).to.be.true; + expect(consumerApplySpy).toHaveBeenCalled(); }); }); describe('createPipesFn', () => { describe('when "paramsOptions" is empty', () => { it('returns null', async () => { const pipesFn = contextCreator.createPipesFn([], []); - expect(pipesFn).to.be.null; + expect(pipesFn).toBeNull(); }); }); describe('when "paramsOptions" is not empty', () => { @@ -194,7 +193,7 @@ describe('ExternalContextCreator', () => { ], )!; await pipesFn([]); - expect(pipesFn).to.be.a('function'); + expect(pipesFn).toBeTypeOf('function'); }); }); }); @@ -206,23 +205,21 @@ describe('ExternalContextCreator', () => { const value = 100; expect( await contextCreator.transformToResult(Promise.resolve(value)), - ).to.be.eq(100); + ).toBe(100); }); }); describe('is Observable', () => { it('should return Promise', async () => { const value = 100; - expect(await contextCreator.transformToResult(of(value))).to.be.eq( - 100, - ); + expect(await contextCreator.transformToResult(of(value))).toBe(100); }); }); describe('is value', () => { it('should return Promise', async () => { const value = 100; - expect(await contextCreator.transformToResult(value)).to.be.eq(100); + expect(await contextCreator.transformToResult(value)).toBe(100); }); }); }); diff --git a/packages/core/test/helpers/external-proxy.spec.ts b/packages/core/test/helpers/external-proxy.spec.ts index 200ee23229f..82ee60b0f28 100644 --- a/packages/core/test/helpers/external-proxy.spec.ts +++ b/packages/core/test/helpers/external-proxy.spec.ts @@ -1,44 +1,77 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { HttpException } from '../../../common/exceptions/http.exception'; -import { ExternalExceptionsHandler } from '../../exceptions/external-exceptions-handler'; -import { ExternalErrorProxy } from '../../helpers/external-proxy'; +import { HttpException } from '../../../common/exceptions/http.exception.js'; +import { ExternalExceptionsHandler } from '../../exceptions/external-exceptions-handler.js'; +import { ExternalErrorProxy } from '../../helpers/external-proxy.js'; describe('ExternalErrorProxy', () => { let externalErrorProxy: ExternalErrorProxy; - let handlerMock: sinon.SinonMock; let handler: ExternalExceptionsHandler; beforeEach(() => { handler = new ExternalExceptionsHandler(); - handlerMock = sinon.mock(handler); externalErrorProxy = new ExternalErrorProxy(); }); describe('createProxy', () => { it('should method return thunk', () => { const proxy = externalErrorProxy.createProxy(() => {}, handler); - expect(typeof proxy === 'function').to.be.true; + expect(typeof proxy === 'function').toBe(true); }); it('should method encapsulate callback passed as argument', async () => { - const expectation = handlerMock.expects('next').once(); + const nextSpy = vi + .spyOn(handler, 'next') + .mockImplementation(async () => {}); const proxy = externalErrorProxy.createProxy((req, res, next) => { throw new HttpException('test', 500); }, handler); await proxy(null, null, null); - expectation.verify(); + expect(nextSpy).toHaveBeenCalledOnce(); }); it('should method encapsulate async callback passed as argument', async () => { - const expectation = handlerMock.expects('next').once(); + const nextSpy = vi + .spyOn(handler, 'next') + .mockImplementation(async () => {}); const proxy = externalErrorProxy.createProxy(async (req, res, next) => { throw new HttpException('test', 500); }, handler); await proxy(null, null, null); - expectation.verify(); + expect(nextSpy).toHaveBeenCalledOnce(); + }); + + it('should return the value when callback succeeds', async () => { + const proxy = externalErrorProxy.createProxy(() => 'success', handler); + const result = await proxy(); + expect(result).toBe('success'); + }); + + it('should return value from async callback', async () => { + const proxy = externalErrorProxy.createProxy( + async () => 'async-result', + handler, + ); + const result = await proxy(); + expect(result).toBe('async-result'); + }); + + it('should pass a type to the ExecutionContextHost on error', async () => { + const nextSpy = vi + .spyOn(handler, 'next') + .mockImplementation(async () => {}); + const proxy = externalErrorProxy.createProxy( + () => { + throw new HttpException('test', 400); + }, + handler, + 'ws', + ); + await proxy('arg1'); + + expect(nextSpy).toHaveBeenCalledOnce(); + const contextArg = nextSpy.mock.calls[0][1]; + expect(contextArg.getType()).toBe('ws'); }); }); }); diff --git a/packages/core/test/helpers/http-adapter-host.spec.ts b/packages/core/test/helpers/http-adapter-host.spec.ts new file mode 100644 index 00000000000..ee84b1cf1a0 --- /dev/null +++ b/packages/core/test/helpers/http-adapter-host.spec.ts @@ -0,0 +1,73 @@ +import { firstValueFrom } from 'rxjs'; +import { HttpAdapterHost } from '../../helpers/http-adapter-host.js'; + +describe('HttpAdapterHost', () => { + let host: HttpAdapterHost; + + beforeEach(() => { + host = new HttpAdapterHost(); + }); + + describe('httpAdapter accessors', () => { + it('should return undefined when no adapter is set', () => { + expect(host.httpAdapter).toBeUndefined(); + }); + + it('should store and retrieve the adapter', () => { + const adapter = { name: 'express' } as any; + host.httpAdapter = adapter; + + expect(host.httpAdapter).toBe(adapter); + }); + }); + + describe('init$', () => { + it('should emit when httpAdapter is set', async () => { + const promise = firstValueFrom(host.init$); + + host.httpAdapter = { name: 'express' } as any; + + await expect(promise).resolves.toBeUndefined(); + }); + + it('should replay to late subscribers', async () => { + host.httpAdapter = { name: 'express' } as any; + + // Subscribe after the adapter was already set + const result = await firstValueFrom(host.init$); + expect(result).toBeUndefined(); + }); + }); + + describe('listening accessors', () => { + it('should default to false', () => { + expect(host.listening).toBe(false); + }); + + it('should update when set to true', () => { + host.listening = true; + expect(host.listening).toBe(true); + }); + }); + + describe('listen$', () => { + it('should emit when listening is set to true', async () => { + const promise = firstValueFrom(host.listen$); + + host.listening = true; + + await expect(promise).resolves.toBeUndefined(); + }); + + it('should not emit when listening is set to false', () => { + let emitted = false; + host.listen$.subscribe(() => { + emitted = true; + }); + + host.listening = false; + + expect(emitted).toBe(false); + }); + }); +}); diff --git a/packages/core/test/helpers/load-adapter.spec.ts b/packages/core/test/helpers/load-adapter.spec.ts new file mode 100644 index 00000000000..544afc1334b --- /dev/null +++ b/packages/core/test/helpers/load-adapter.spec.ts @@ -0,0 +1,62 @@ +import { Logger } from '@nestjs/common'; +import { loadAdapter } from '../../helpers/load-adapter.js'; + +describe('loadAdapter', () => { + let exitSpy: ReturnType; + let errorSpy: ReturnType; + + beforeEach(() => { + exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any); + errorSpy = vi.spyOn(Logger.prototype, 'error').mockImplementation(() => {}); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should return the module when import succeeds', async () => { + const result = await loadAdapter('path', 'TestTransport'); + expect(result).toBeDefined(); + expect(exitSpy).not.toHaveBeenCalled(); + }); + + it('should use loaderFn when provided', async () => { + const fakeModule = { adapter: true }; + const loaderFn = vi.fn().mockResolvedValue(fakeModule); + + const result = await loadAdapter('anything', 'TestTransport', loaderFn); + expect(result).toBe(fakeModule); + expect(loaderFn).toHaveBeenCalledOnce(); + expect(exitSpy).not.toHaveBeenCalled(); + }); + + it('should log error and exit when import fails', async () => { + await loadAdapter('nonexistent-package-xyz', 'TestTransport'); + + expect(errorSpy).toHaveBeenCalledWith( + expect.stringContaining('No driver (TestTransport) has been selected'), + ); + expect(exitSpy).toHaveBeenCalledWith(1); + }); + + it('should log error and exit when loaderFn throws', async () => { + const loaderFn = vi.fn().mockRejectedValue(new Error('boom')); + + await loadAdapter('anything', 'TestTransport', loaderFn); + + expect(errorSpy).toHaveBeenCalledWith( + expect.stringContaining('No driver (TestTransport) has been selected'), + ); + expect(exitSpy).toHaveBeenCalledWith(1); + }); + + it('should include install instruction in error message', async () => { + const loaderFn = vi.fn().mockRejectedValue(new Error('boom')); + + await loadAdapter('@nestjs/platform-express', 'HTTP', loaderFn); + + expect(errorSpy).toHaveBeenCalledWith( + expect.stringContaining('$ npm install @nestjs/platform-express'), + ); + }); +}); diff --git a/packages/core/test/helpers/optional-require.spec.ts b/packages/core/test/helpers/optional-require.spec.ts new file mode 100644 index 00000000000..06e55bd202c --- /dev/null +++ b/packages/core/test/helpers/optional-require.spec.ts @@ -0,0 +1,36 @@ +import { optionalRequire } from '../../helpers/optional-require.js'; + +describe('optionalRequire', () => { + it('should return the module when import succeeds', async () => { + const result = await optionalRequire('path'); + expect(result).toBeDefined(); + expect( + typeof result.resolve === 'function' || typeof result.join === 'function', + ).toBe(true); + }); + + it('should return empty object when package does not exist', async () => { + const result = await optionalRequire('non-existent-package-xyz-123'); + expect(result).toEqual({}); + }); + + it('should use loaderFn when provided', async () => { + const mockModule = { myFn: () => 42 }; + const loaderFn = vi.fn().mockResolvedValue(mockModule); + const result = await optionalRequire('anything', loaderFn); + expect(result).toBe(mockModule); + expect(loaderFn).toHaveBeenCalledOnce(); + }); + + it('should return empty object when loaderFn throws', async () => { + const loaderFn = vi.fn().mockRejectedValue(new Error('load failed')); + const result = await optionalRequire('anything', loaderFn); + expect(result).toEqual({}); + }); + + it('should not call loaderFn if not provided', async () => { + // Just verify it doesn't throw with a valid package and no loaderFn + const result = await optionalRequire('fs'); + expect(result).toBeDefined(); + }); +}); diff --git a/packages/core/test/helpers/rethrow.spec.ts b/packages/core/test/helpers/rethrow.spec.ts new file mode 100644 index 00000000000..9a7ad214e71 --- /dev/null +++ b/packages/core/test/helpers/rethrow.spec.ts @@ -0,0 +1,34 @@ +import { rethrow } from '../../helpers/rethrow.js'; + +describe('rethrow', () => { + it('should throw an Error instance', () => { + const err = new Error('test error'); + expect(() => rethrow(err)).toThrow(err); + }); + + it('should throw a string', () => { + expect(() => rethrow('string error')).toThrow('string error'); + }); + + it('should throw a number', () => { + expect(() => rethrow(42)).toThrow(); + }); + + it('should throw null', () => { + expect(() => rethrow(null)).toThrow(); + }); + + it('should throw undefined', () => { + expect(() => rethrow(undefined)).toThrow(); + }); + + it('should throw a custom object', () => { + const obj = { code: 'FAIL', message: 'custom error' }; + try { + rethrow(obj); + expect.unreachable('should have thrown'); + } catch (e) { + expect(e).toBe(obj); + } + }); +}); diff --git a/packages/core/test/helpers/router-method-factory.spec.ts b/packages/core/test/helpers/router-method-factory.spec.ts index 6b3edfad54d..2d00267e323 100644 --- a/packages/core/test/helpers/router-method-factory.spec.ts +++ b/packages/core/test/helpers/router-method-factory.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { RequestMethod } from '../../../common/enums/request-method.enum'; -import { RouterMethodFactory } from '../../helpers/router-method-factory'; +import { RequestMethod } from '../../../common/enums/request-method.enum.js'; +import { RouterMethodFactory } from '../../helpers/router-method-factory.js'; describe('RouterMethodFactory', () => { let factory: RouterMethodFactory; @@ -28,25 +27,21 @@ describe('RouterMethodFactory', () => { }); it('should return proper method', () => { - expect(factory.get(target, RequestMethod.DELETE)).to.equal(target.delete); - expect(factory.get(target, RequestMethod.POST)).to.equal(target.post); - expect(factory.get(target, RequestMethod.ALL)).to.equal(target.all); - expect(factory.get(target, RequestMethod.PUT)).to.equal(target.put); - expect(factory.get(target, RequestMethod.GET)).to.equal(target.get); - expect(factory.get(target, RequestMethod.PATCH)).to.equal(target.patch); - expect(factory.get(target, RequestMethod.OPTIONS)).to.equal(target.options); - expect(factory.get(target, RequestMethod.HEAD)).to.equal(target.head); - expect(factory.get(target, RequestMethod.PROPFIND)).to.equal( - target.propfind, - ); - expect(factory.get(target, RequestMethod.PROPPATCH)).to.equal( - target.proppatch, - ); - expect(factory.get(target, RequestMethod.MKCOL)).to.equal(target.mkcol); - expect(factory.get(target, RequestMethod.COPY)).to.equal(target.copy); - expect(factory.get(target, RequestMethod.MOVE)).to.equal(target.move); - expect(factory.get(target, RequestMethod.LOCK)).to.equal(target.lock); - expect(factory.get(target, RequestMethod.UNLOCK)).to.equal(target.unlock); - expect(factory.get(target, -1 as any)).to.equal(target.use); + expect(factory.get(target, RequestMethod.DELETE)).toBe(target.delete); + expect(factory.get(target, RequestMethod.POST)).toBe(target.post); + expect(factory.get(target, RequestMethod.ALL)).toBe(target.all); + expect(factory.get(target, RequestMethod.PUT)).toBe(target.put); + expect(factory.get(target, RequestMethod.GET)).toBe(target.get); + expect(factory.get(target, RequestMethod.PATCH)).toBe(target.patch); + expect(factory.get(target, RequestMethod.OPTIONS)).toBe(target.options); + expect(factory.get(target, RequestMethod.HEAD)).toBe(target.head); + expect(factory.get(target, RequestMethod.PROPFIND)).toBe(target.propfind); + expect(factory.get(target, RequestMethod.PROPPATCH)).toBe(target.proppatch); + expect(factory.get(target, RequestMethod.MKCOL)).toBe(target.mkcol); + expect(factory.get(target, RequestMethod.COPY)).toBe(target.copy); + expect(factory.get(target, RequestMethod.MOVE)).toBe(target.move); + expect(factory.get(target, RequestMethod.LOCK)).toBe(target.lock); + expect(factory.get(target, RequestMethod.UNLOCK)).toBe(target.unlock); + expect(factory.get(target, -1 as any)).toBe(target.use); }); }); diff --git a/packages/core/test/hooks/before-app-shutdown.hook.spec.ts b/packages/core/test/hooks/before-app-shutdown.hook.spec.ts index 4d16581b1b9..517630b5a22 100644 --- a/packages/core/test/hooks/before-app-shutdown.hook.spec.ts +++ b/packages/core/test/hooks/before-app-shutdown.hook.spec.ts @@ -1,9 +1,7 @@ import { BeforeApplicationShutdown } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { callBeforeAppShutdownHook } from '../../hooks/before-app-shutdown.hook'; -import { NestContainer } from '../../injector/container'; -import { Module } from '../../injector/module'; +import { callBeforeAppShutdownHook } from '../../hooks/before-app-shutdown.hook.js'; +import { NestContainer } from '../../injector/container.js'; +import { Module } from '../../injector/module.js'; class SampleProvider implements BeforeApplicationShutdown { beforeApplicationShutdown(signal?: string) {} @@ -40,10 +38,10 @@ describe('BeforeAppShutdown', () => { it('should call "beforeApplicationShutdown" hook for the entire module', async () => { const signal = 'SIGTERM'; - const hookSpy = sinon.spy(sampleProvider, 'beforeApplicationShutdown'); + const hookSpy = vi.spyOn(sampleProvider, 'beforeApplicationShutdown'); await callBeforeAppShutdownHook(moduleRef, signal); - expect(hookSpy.calledWith(signal)).to.be.true; + expect(hookSpy).toHaveBeenCalledWith(signal); }); }); }); diff --git a/packages/core/test/hooks/on-app-bootstrap.hook.spec.ts b/packages/core/test/hooks/on-app-bootstrap.hook.spec.ts index 578fd2d30c6..2d628787657 100644 --- a/packages/core/test/hooks/on-app-bootstrap.hook.spec.ts +++ b/packages/core/test/hooks/on-app-bootstrap.hook.spec.ts @@ -1,9 +1,7 @@ import { OnApplicationBootstrap } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { callModuleBootstrapHook } from '../../hooks/on-app-bootstrap.hook'; -import { NestContainer } from '../../injector/container'; -import { Module } from '../../injector/module'; +import { callModuleBootstrapHook } from '../../hooks/on-app-bootstrap.hook.js'; +import { NestContainer } from '../../injector/container.js'; +import { Module } from '../../injector/module.js'; class SampleProvider implements OnApplicationBootstrap { onApplicationBootstrap() {} @@ -38,10 +36,10 @@ describe('OnApplicationBootstrap', () => { describe('callModuleBootstrapHook', () => { it('should call "onApplicationBootstrap" hook for the entire module', async () => { - const hookSpy = sinon.spy(sampleProvider, 'onApplicationBootstrap'); + const hookSpy = vi.spyOn(sampleProvider, 'onApplicationBootstrap'); await callModuleBootstrapHook(moduleRef); - expect(hookSpy.called).to.be.true; + expect(hookSpy).toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/hooks/on-app-shutdown.hook.spec.ts b/packages/core/test/hooks/on-app-shutdown.hook.spec.ts index a680b9cf9c6..6cb0ab7d2a9 100644 --- a/packages/core/test/hooks/on-app-shutdown.hook.spec.ts +++ b/packages/core/test/hooks/on-app-shutdown.hook.spec.ts @@ -1,9 +1,7 @@ import { OnApplicationShutdown } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { callAppShutdownHook } from '../../hooks/on-app-shutdown.hook'; -import { NestContainer } from '../../injector/container'; -import { Module } from '../../injector/module'; +import { callAppShutdownHook } from '../../hooks/on-app-shutdown.hook.js'; +import { NestContainer } from '../../injector/container.js'; +import { Module } from '../../injector/module.js'; class SampleProvider implements OnApplicationShutdown { onApplicationShutdown() {} @@ -38,10 +36,10 @@ describe('OnApplicationShutdown', () => { describe('callAppShutdownHook', () => { it('should call "onApplicationShutdown" hook for the entire module', async () => { - const hookSpy = sinon.spy(sampleProvider, 'onApplicationShutdown'); + const hookSpy = vi.spyOn(sampleProvider, 'onApplicationShutdown'); await callAppShutdownHook(moduleRef); - expect(hookSpy.called).to.be.true; + expect(hookSpy).toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/hooks/on-module-destroy.hook.spec.ts b/packages/core/test/hooks/on-module-destroy.hook.spec.ts index 6d4ad844a04..fad18ec4145 100644 --- a/packages/core/test/hooks/on-module-destroy.hook.spec.ts +++ b/packages/core/test/hooks/on-module-destroy.hook.spec.ts @@ -1,9 +1,7 @@ import { OnModuleDestroy } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { callModuleDestroyHook } from '../../hooks/on-module-destroy.hook'; -import { NestContainer } from '../../injector/container'; -import { Module } from '../../injector/module'; +import { callModuleDestroyHook } from '../../hooks/on-module-destroy.hook.js'; +import { NestContainer } from '../../injector/container.js'; +import { Module } from '../../injector/module.js'; class SampleProvider implements OnModuleDestroy { onModuleDestroy() {} @@ -38,10 +36,10 @@ describe('OnModuleDestroy', () => { describe('callModuleDestroyHook', () => { it('should call "onModuleDestroy" hook for the entire module', async () => { - const hookSpy = sinon.spy(sampleProvider, 'onModuleDestroy'); + const hookSpy = vi.spyOn(sampleProvider, 'onModuleDestroy'); await callModuleDestroyHook(moduleRef); - expect(hookSpy.called).to.be.true; + expect(hookSpy).toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/hooks/on-module-init.hook.spec.ts b/packages/core/test/hooks/on-module-init.hook.spec.ts index 8480f1c60de..54c154179f5 100644 --- a/packages/core/test/hooks/on-module-init.hook.spec.ts +++ b/packages/core/test/hooks/on-module-init.hook.spec.ts @@ -1,9 +1,7 @@ import { OnModuleInit } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { callModuleInitHook } from '../../hooks/on-module-init.hook'; -import { NestContainer } from '../../injector/container'; -import { Module } from '../../injector/module'; +import { callModuleInitHook } from '../../hooks/on-module-init.hook.js'; +import { NestContainer } from '../../injector/container.js'; +import { Module } from '../../injector/module.js'; class SampleProvider implements OnModuleInit { onModuleInit() {} @@ -38,10 +36,10 @@ describe('OnModuleInit', () => { describe('callModuleInitHook', () => { it('should call "onModuleInit" hook for the entire module', async () => { - const hookSpy = sinon.spy(sampleProvider, 'onModuleInit'); + const hookSpy = vi.spyOn(sampleProvider, 'onModuleInit'); await callModuleInitHook(moduleRef); - expect(hookSpy.called).to.be.true; + expect(hookSpy).toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/injector/compiler.spec.ts b/packages/core/test/injector/compiler.spec.ts index 2929c49e11b..26a70452dea 100644 --- a/packages/core/test/injector/compiler.spec.ts +++ b/packages/core/test/injector/compiler.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { ModuleCompiler } from '../../injector/compiler'; -import { ByReferenceModuleOpaqueKeyFactory } from '../../injector/opaque-key-factory/by-reference-module-opaque-key-factory'; +import { ModuleCompiler } from '../../injector/compiler.js'; +import { ByReferenceModuleOpaqueKeyFactory } from '../../injector/opaque-key-factory/by-reference-module-opaque-key-factory.js'; describe('ModuleCompiler', () => { let compiler: ModuleCompiler; @@ -13,7 +12,7 @@ describe('ModuleCompiler', () => { it('should return object with "type" and "dynamicMetadata" property', () => { const obj = { module: 'test', providers: [] }; const { module, ...dynamicMetadata } = obj; - expect(compiler.extractMetadata(obj as any)).to.be.deep.equal({ + expect(compiler.extractMetadata(obj as any)).toEqual({ type: module, dynamicMetadata, }); @@ -22,7 +21,7 @@ describe('ModuleCompiler', () => { describe('when module is a not dynamic module', () => { it('should return object with "type" property', () => { const type = 'test'; - expect(compiler.extractMetadata(type as any)).to.be.deep.equal({ + expect(compiler.extractMetadata(type as any)).toEqual({ type, dynamicMetadata: undefined, }); @@ -33,12 +32,12 @@ describe('ModuleCompiler', () => { describe('isDynamicModule', () => { describe('when module is a dynamic module', () => { it('should return true', () => { - expect(compiler.isDynamicModule({ module: true } as any)).to.be.true; + expect(compiler.isDynamicModule({ module: true } as any)).toBe(true); }); }); describe('when module is a dynamic module', () => { it('should return false', () => { - expect(compiler.isDynamicModule({ x: true } as any)).to.be.false; + expect(compiler.isDynamicModule({ x: true } as any)).toBe(false); }); }); }); diff --git a/packages/core/test/injector/container.spec.ts b/packages/core/test/injector/container.spec.ts index fe77d3ebb01..2fb70b1d19f 100644 --- a/packages/core/test/injector/container.spec.ts +++ b/packages/core/test/injector/container.spec.ts @@ -1,11 +1,9 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Module } from '../../../common/decorators/modules/module.decorator'; -import { Global } from '../../../common/index'; -import { CircularDependencyException } from '../../errors/exceptions/circular-dependency.exception'; -import { UnknownModuleException } from '../../errors/exceptions/unknown-module.exception'; -import { NestContainer } from '../../injector/container'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +import { Module } from '../../../common/decorators/modules/module.decorator.js'; +import { Global } from '../../../common/index.js'; +import { CircularDependencyException } from '../../errors/exceptions/circular-dependency.exception.js'; +import { UnknownModuleException } from '../../errors/exceptions/unknown-module.exception.js'; +import { NestContainer } from '../../injector/container.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('NestContainer', () => { let container: NestContainer; @@ -24,19 +22,19 @@ describe('NestContainer', () => { }); it('should "addProvider" throw "UnknownModuleException" when module is not stored in collection', () => { - expect(() => container.addProvider({} as any, 'TestModule')).throw( + expect(() => container.addProvider({} as any, 'TestModule')).toThrow( UnknownModuleException, ); }); it('should "addProvider" throw "CircularDependencyException" when provider is nil', () => { - expect(() => container.addProvider(null!, 'TestModule')).throw( + expect(() => container.addProvider(null!, 'TestModule')).toThrow( CircularDependencyException, ); }); it('should "addController" throw "UnknownModuleException" when module is not stored in collection', () => { - expect(() => container.addController(null!, 'TestModule')).throw( + expect(() => container.addController(null!, 'TestModule')).toThrow( UnknownModuleException, ); }); @@ -44,43 +42,43 @@ describe('NestContainer', () => { it('should "addExportedProviderOrModule" throw "UnknownModuleException" when module is not stored in collection', () => { expect(() => container.addExportedProviderOrModule(null!, 'TestModule'), - ).throw(UnknownModuleException); + ).toThrow(UnknownModuleException); }); it('should "addInjectable" throw "UnknownModuleException" when module is not stored in collection', () => { - expect(() => container.addInjectable(null!, 'TestModule', null!)).throw( + expect(() => container.addInjectable(null!, 'TestModule', null!)).toThrow( UnknownModuleException, ); }); describe('clear', () => { it('should call `clear` on modules collection', () => { - const clearSpy = sinon.spy(untypedContainer.modules, 'clear'); + const clearSpy = vi.spyOn(untypedContainer.modules, 'clear'); container.clear(); - expect(clearSpy.called).to.be.true; + expect(clearSpy).toHaveBeenCalled(); }); }); describe('addModule', () => { it('should not add module if already exists in collection', async () => { const modules = new Map(); - const setSpy = sinon.spy(modules, 'set'); + const setSpy = vi.spyOn(modules, 'set'); untypedContainer.modules = modules; await container.addModule(TestModule as any, []); await container.addModule(TestModule as any, []); - expect(setSpy.calledOnce).to.be.true; + expect(setSpy).toHaveBeenCalledOnce(); }); - it('should throw an exception when metatype is not defined', () => { - expect(container.addModule(undefined!, [])).to.eventually.throws(); + it('should throw an exception when metatype is not defined', async () => { + await expect(container.addModule(undefined!, [])).rejects.toThrow(); }); it('should add global module when module is global', async () => { - const addGlobalModuleSpy = sinon.spy(container, 'addGlobalModule'); + const addGlobalModuleSpy = vi.spyOn(container, 'addGlobalModule'); await container.addModule(GlobalTestModule as any, []); - expect(addGlobalModuleSpy.calledOnce).to.be.true; + expect(addGlobalModuleSpy).toHaveBeenCalledOnce(); }); }); @@ -90,7 +88,7 @@ describe('NestContainer', () => { class ReplaceTestModule {} const modules = new Map(); - const setSpy = sinon.spy(modules, 'set'); + const setSpy = vi.spyOn(modules, 'set'); untypedContainer.modules = modules; await container.addModule(TestModule as any, []); @@ -100,35 +98,36 @@ describe('NestContainer', () => { [], ); - expect(setSpy.calledTwice).to.be.true; + expect(setSpy).toHaveBeenCalledTimes(2); }); - it('should throw an exception when metatype is not defined', () => { - expect(container.addModule(undefined!, [])).to.eventually.throws(); + it('should throw an exception when metatype is not defined', async () => { + await expect(container.addModule(undefined!, [])).rejects.toThrow(); }); it('should add global module when module is global', async () => { - const addGlobalModuleSpy = sinon.spy(container, 'addGlobalModule'); + const addGlobalModuleSpy = vi.spyOn(container, 'addGlobalModule'); await container.addModule(GlobalTestModule as any, []); - expect(addGlobalModuleSpy.calledOnce).to.be.true; + expect(addGlobalModuleSpy).toHaveBeenCalledOnce(); }); }); describe('isGlobalModule', () => { describe('when module is not globally scoped', () => { it('should return false', () => { - expect(container.isGlobalModule(TestModule)).to.be.false; + expect(container.isGlobalModule(TestModule)).toBe(false); }); }); describe('when module is globally scoped', () => { it('should return true', () => { - expect(container.isGlobalModule(GlobalTestModule)).to.be.true; + expect(container.isGlobalModule(GlobalTestModule)).toBe(true); }); }); describe('when dynamic module is globally scoped', () => { it('should return true', () => { - expect(container.isGlobalModule(TestModule, { global: true })).to.be - .true; + expect(container.isGlobalModule(TestModule, { global: true })).toBe( + true, + ); }); }); }); @@ -141,30 +140,30 @@ describe('NestContainer', () => { container.addGlobalModule(global1 as any); container.addGlobalModule(global2 as any); - const bindGlobalModuleToModuleSpy = sinon.spy( + const bindGlobalModuleToModuleSpy = vi.spyOn( container, 'bindGlobalModuleToModule', ); container.bindGlobalsToImports({ - addImport: sinon.spy(), + addImport: vi.fn(), } as any); - expect(bindGlobalModuleToModuleSpy.calledTwice).to.be.true; + expect(bindGlobalModuleToModuleSpy).toHaveBeenCalledTimes(2); }); }); describe('bindGlobalModuleToModule', () => { describe('when "module" is not "globalModule"', () => { it('should call "addImport"', () => { - const module = { addImport: sinon.spy() }; + const module = { addImport: vi.fn() }; container.bindGlobalModuleToModule(module as any, null!); - expect(module.addImport.calledOnce).to.be.true; + expect(module.addImport).toHaveBeenCalledOnce(); }); }); describe('when "module" is "globalModule"', () => { it('should not call "addImport"', () => { - const module = { addImport: sinon.spy() }; + const module = { addImport: vi.fn() }; container.bindGlobalModuleToModule(module as any, module as any); - expect(module.addImport.calledOnce).to.be.false; + expect(module.addImport).not.toHaveBeenCalled(); }); }); }); @@ -180,18 +179,18 @@ describe('NestContainer', () => { }); describe('when dynamic metadata exists', () => { it('should add to the dynamic metadata collection', async () => { - const addSpy = sinon.spy(collection, 'set'); + const addSpy = vi.spyOn(collection, 'set'); const dynamicMetadata = { module: null! }; await container.addDynamicMetadata(token, dynamicMetadata, []); - expect(addSpy.calledWith(token, dynamicMetadata)).to.be.true; + expect(addSpy).toHaveBeenCalledWith(token, dynamicMetadata); }); }); describe('when dynamic metadata does not exists', () => { it('should not add to the dynamic metadata collection', async () => { - const addSpy = sinon.spy(collection, 'set'); + const addSpy = vi.spyOn(collection, 'set'); await container.addDynamicMetadata(token, null!, []); - expect(addSpy.called).to.be.false; + expect(addSpy).not.toHaveBeenCalled(); }); }); }); @@ -200,23 +199,23 @@ describe('NestContainer', () => { describe('addDynamicModules', () => { describe('when array is empty/undefined', () => { it('should not call "addModule"', async () => { - const addModuleSpy = sinon.spy(container, 'addModule'); + const addModuleSpy = vi.spyOn(container, 'addModule'); await container.addDynamicModules(undefined!, []); - expect(addModuleSpy.called).to.be.false; + expect(addModuleSpy).not.toHaveBeenCalled(); }); }); describe('when array is not empty/undefined', () => { it('should call "addModule"', async () => { - const addModuleSpy = sinon.spy(container, 'addModule'); + const addModuleSpy = vi.spyOn(container, 'addModule'); await container.addDynamicModules([Test] as any, []); - expect(addModuleSpy.called).to.be.true; + expect(addModuleSpy).toHaveBeenCalled(); }); }); }); describe('get applicationConfig', () => { it('should return ApplicationConfig instance', () => { - expect(container.applicationConfig).to.be.eql( + expect(container.applicationConfig).toEqual( untypedContainer._applicationConfig, ); }); @@ -228,7 +227,7 @@ describe('NestContainer', () => { container.setHttpAdapter(httpAdapter); const internalStorage = untypedContainer.internalProvidersStorage; - expect(internalStorage.httpAdapter).to.be.eql(httpAdapter); + expect(internalStorage.httpAdapter).toEqual(httpAdapter); }); }); @@ -238,7 +237,7 @@ describe('NestContainer', () => { const value = {}; container.getModules().set(key, value as any); - expect(container.getModuleByKey(key)).to.be.eql(value); + expect(container.getModuleByKey(key)).toEqual(value); }); }); @@ -246,7 +245,7 @@ describe('NestContainer', () => { it('should register core module ref', () => { const ref = {} as any; container.registerCoreModuleRef(ref); - expect(untypedContainer.internalCoreModule).to.be.eql(ref); + expect(untypedContainer.internalCoreModule).toEqual(ref); }); }); }); diff --git a/packages/core/test/injector/helpers/provider-classifier.spec.ts b/packages/core/test/injector/helpers/provider-classifier.spec.ts index cd7e9a6b10a..75c76c4c521 100644 --- a/packages/core/test/injector/helpers/provider-classifier.spec.ts +++ b/packages/core/test/injector/helpers/provider-classifier.spec.ts @@ -1,10 +1,9 @@ import { ClassProvider, FactoryProvider, ValueProvider } from '@nestjs/common'; -import { expect } from 'chai'; import { isClassProvider, isFactoryProvider, isValueProvider, -} from '../../../injector/helpers/provider-classifier'; +} from '../../../injector/helpers/provider-classifier.js'; describe('provider classifier', () => { describe('isClassProvider', () => { @@ -14,7 +13,7 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isClassProvider(classProvider)).to.be.true; + expect(isClassProvider(classProvider)).toBe(true); }); it('should return false if useClass is undefined', () => { @@ -23,7 +22,7 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isClassProvider(classProvider)).to.be.false; + expect(isClassProvider(classProvider)).toBe(false); }); it('should return false if useClass is not present', () => { @@ -31,13 +30,13 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isClassProvider(classProvider as ClassProvider)).to.be.false; + expect(isClassProvider(classProvider as ClassProvider)).toBe(false); }); it('should return false if provider is undefined', () => { const classProvider = undefined!; - expect(isClassProvider(classProvider)).to.be.false; + expect(isClassProvider(classProvider)).toBe(false); }); }); @@ -48,7 +47,7 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isValueProvider(valueProvider)).to.be.true; + expect(isValueProvider(valueProvider)).toBe(true); }); it('should return true if useValue is "false"', () => { @@ -57,7 +56,7 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isValueProvider(valueProvider)).to.be.true; + expect(isValueProvider(valueProvider)).toBe(true); }); it('should return true if useValue is "null"', () => { @@ -66,7 +65,7 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isValueProvider(valueProvider)).to.be.true; + expect(isValueProvider(valueProvider)).toBe(true); }); it('should return true if useValue is an empty string', () => { @@ -75,7 +74,7 @@ describe('provider classifier', () => { provide: '', }; - expect(isValueProvider(valueProvider)).to.be.true; + expect(isValueProvider(valueProvider)).toBe(true); }); it('should return false if useValue is undefined', () => { @@ -84,7 +83,7 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isValueProvider(valueProvider)).to.be.false; + expect(isValueProvider(valueProvider)).toBe(false); }); it('should return false if useValue is not present', () => { @@ -92,13 +91,13 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isValueProvider(valueProvider as ValueProvider)).to.be.false; + expect(isValueProvider(valueProvider as ValueProvider)).toBe(false); }); it('should return false if provider is undefined', () => { const valueProvider = undefined!; - expect(isValueProvider(valueProvider as ValueProvider)).to.be.false; + expect(isValueProvider(valueProvider as ValueProvider)).toBe(false); }); }); @@ -109,7 +108,7 @@ describe('provider classifier', () => { useFactory: () => {}, }; - expect(isFactoryProvider(factoryProvider)).to.be.true; + expect(isFactoryProvider(factoryProvider)).toBe(true); }); it('should return false if useFactory is not present', () => { @@ -117,7 +116,7 @@ describe('provider classifier', () => { provide: 'token', }; - expect(isFactoryProvider(factoryProvider as FactoryProvider)).to.be.false; + expect(isFactoryProvider(factoryProvider as FactoryProvider)).toBe(false); }); it('should return false if useFactory is undefined', () => { @@ -126,7 +125,7 @@ describe('provider classifier', () => { useFactory: undefined!, }; - expect(isFactoryProvider(factoryProvider)).to.be.false; + expect(isFactoryProvider(factoryProvider)).toBe(false); }); }); }); diff --git a/packages/core/test/injector/helpers/silent-logger.spec.ts b/packages/core/test/injector/helpers/silent-logger.spec.ts index a5ff67a12ea..db5911be782 100644 --- a/packages/core/test/injector/helpers/silent-logger.spec.ts +++ b/packages/core/test/injector/helpers/silent-logger.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; import { Logger } from '@nestjs/common'; -import { SilentLogger } from '../../../injector/helpers/silent-logger'; +import { SilentLogger } from '../../../injector/helpers/silent-logger.js'; describe('SilentLogger', () => { let silentLogger: SilentLogger; @@ -10,43 +9,43 @@ describe('SilentLogger', () => { }); it('should be an instance of Logger', () => { - expect(silentLogger).to.be.instanceOf(Logger); + expect(silentLogger).toBeInstanceOf(Logger); }); describe('logging methods', () => { it('should have log method that does nothing', () => { - expect(() => silentLogger.log()).to.not.throw(); - expect(silentLogger.log()).to.be.undefined; + expect(() => silentLogger.log()).not.toThrow(); + expect(silentLogger.log()).toBeUndefined(); }); it('should have error method that does nothing', () => { - expect(() => silentLogger.error()).to.not.throw(); - expect(silentLogger.error()).to.be.undefined; + expect(() => silentLogger.error()).not.toThrow(); + expect(silentLogger.error()).toBeUndefined(); }); it('should have warn method that does nothing', () => { - expect(() => silentLogger.warn()).to.not.throw(); - expect(silentLogger.warn()).to.be.undefined; + expect(() => silentLogger.warn()).not.toThrow(); + expect(silentLogger.warn()).toBeUndefined(); }); it('should have debug method that does nothing', () => { - expect(() => silentLogger.debug()).to.not.throw(); - expect(silentLogger.debug()).to.be.undefined; + expect(() => silentLogger.debug()).not.toThrow(); + expect(silentLogger.debug()).toBeUndefined(); }); it('should have verbose method that does nothing', () => { - expect(() => silentLogger.verbose()).to.not.throw(); - expect(silentLogger.verbose()).to.be.undefined; + expect(() => silentLogger.verbose()).not.toThrow(); + expect(silentLogger.verbose()).toBeUndefined(); }); it('should have fatal method that does nothing', () => { - expect(() => silentLogger.fatal()).to.not.throw(); - expect(silentLogger.fatal()).to.be.undefined; + expect(() => silentLogger.fatal()).not.toThrow(); + expect(silentLogger.fatal()).toBeUndefined(); }); it('should have setLogLevels method that does nothing', () => { - expect(() => silentLogger.setLogLevels()).to.not.throw(); - expect(silentLogger.setLogLevels()).to.be.undefined; + expect(() => silentLogger.setLogLevels()).not.toThrow(); + expect(silentLogger.setLogLevels()).toBeUndefined(); }); }); }); diff --git a/packages/core/test/injector/injector.spec.ts b/packages/core/test/injector/injector.spec.ts index 54b8509d945..075d45b5845 100644 --- a/packages/core/test/injector/injector.spec.ts +++ b/packages/core/test/injector/injector.spec.ts @@ -1,19 +1,13 @@ import { Optional } from '@nestjs/common'; -import { PARAMTYPES_METADATA } from '@nestjs/common/constants'; -import * as chai from 'chai'; -import { expect } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; -import * as sinon from 'sinon'; -import { Inject } from '../../../common/decorators/core/inject.decorator'; -import { Injectable } from '../../../common/decorators/core/injectable.decorator'; -import { STATIC_CONTEXT } from '../../injector/constants'; -import { NestContainer } from '../../injector/container'; -import { Injector, PropertyDependency } from '../../injector/injector'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { Module } from '../../injector/module'; -import { SettlementSignal } from '../../injector/settlement-signal'; - -chai.use(chaiAsPromised); +import { PARAMTYPES_METADATA } from '@nestjs/common/constants.js'; +import { Inject } from '../../../common/decorators/core/inject.decorator.js'; +import { Injectable } from '../../../common/decorators/core/injectable.decorator.js'; +import { STATIC_CONTEXT } from '../../injector/constants.js'; +import { NestContainer } from '../../injector/container.js'; +import { Injector, PropertyDependency } from '../../injector/injector.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { Module } from '../../injector/module.js'; +import { SettlementSignal } from '../../injector/settlement-signal.js'; describe('Injector', () => { let injector: Injector; @@ -31,11 +25,11 @@ describe('Injector', () => { @Injectable() class MainTest { - @Inject() property: DependencyOne; + @Inject(DependencyOne) property: DependencyOne; constructor( public one: DependencyOne, - @Inject() public two: DependencyTwo, + @Inject(DependencyTwo) public two: DependencyTwo, ) {} } @@ -80,9 +74,9 @@ describe('Injector', () => { 'MainTest', ) as InstanceWrapper; - expect(instance.one).instanceof(DependencyOne); - expect(instance.two).instanceof(DependencyTwo); - expect(instance).instanceof(MainTest); + expect(instance.one).toBeInstanceOf(DependencyOne); + expect(instance.two).toBeInstanceOf(DependencyTwo); + expect(instance).toBeInstanceOf(MainTest); }); it('should set "isResolved" property to true after instance initialization', async () => { @@ -90,16 +84,16 @@ describe('Injector', () => { const { isResolved } = ( moduleDeps.providers.get('MainTest') as InstanceWrapper ).getInstanceByContextId(STATIC_CONTEXT); - expect(isResolved).to.be.true; + expect(isResolved).toBe(true); }); - it('should throw RuntimeException when type is not stored in collection', () => { - return expect( + it('should throw RuntimeException when type is not stored in collection', async () => { + await expect( injector.loadInstance({} as any, moduleDeps.providers, moduleDeps), - ).to.eventually.be.rejected; + ).rejects.toBeDefined(); }); - it('should await done$ when "isPending"', () => { + it('should await done$ when "isPending"', async () => { const wrapper = new InstanceWrapper({ name: 'MainTest', metatype: MainTest, @@ -110,12 +104,12 @@ describe('Injector', () => { host.donePromise = Promise.resolve(); host.isPending = true; - expect( + await expect( injector.loadInstance(wrapper, moduleDeps.providers, moduleDeps), - ).to.eventually.not.throw(); + ).resolves.not.toThrow(); }); - it('should await done$ when "isPending" and rethrow an exception (if thrown)', () => { + it('should await done$ when "isPending" and rethrow an exception (if thrown)', async () => { const error = new Error('Test error'); const wrapper = new InstanceWrapper({ name: 'MainTest', @@ -127,9 +121,9 @@ describe('Injector', () => { host.donePromise = Promise.resolve(error); host.isPending = true; - expect( + await expect( injector.loadInstance(wrapper, moduleDeps.providers, moduleDeps), - ).to.eventually.throw(error); + ).rejects.toThrow(error); }); it('should return undefined when metatype is resolved', async () => { @@ -143,7 +137,7 @@ describe('Injector', () => { moduleDeps.providers, moduleDeps, ); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); @@ -168,14 +162,14 @@ describe('Injector', () => { it('should create prototype of instance', () => { injector.loadPrototype(test, moduleDeps.providers); - expect(moduleDeps.providers.get('Test')!.instance).to.deep.equal( + expect(moduleDeps.providers.get('Test')!.instance).toEqual( Object.create(Test.prototype), ); }); it('should return undefined when collection is nil', () => { const result = injector.loadPrototype(test, null!); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); it('should return undefined when target isResolved', () => { @@ -186,7 +180,7 @@ describe('Injector', () => { }), }; const result = injector.loadPrototype(test, collection as any); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); it('should return undefined when "inject" is not nil', () => { @@ -194,28 +188,28 @@ describe('Injector', () => { get: () => new InstanceWrapper({ inject: [] }), }; const result = injector.loadPrototype(test, collection as any); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('resolveSingleParam', () => { it('should throw "RuntimeException" when param is undefined', async () => { - return expect( + await expect( injector.resolveSingleParam( null!, undefined!, { index: 0, dependencies: [] }, null!, ), - ).to.eventually.be.rejected; + ).rejects.toBeDefined(); }); }); describe('loadMiddleware', () => { - let loadInstanceSpy: sinon.SinonSpy; + let loadInstanceSpy: ReturnType; beforeEach(() => { - loadInstanceSpy = sinon.spy(); + loadInstanceSpy = vi.fn(); injector.loadInstance = loadInstanceSpy; }); @@ -230,7 +224,7 @@ describe('Injector', () => { collection as any, null!, ); - expect(loadInstanceSpy.called).to.be.true; + expect(loadInstanceSpy).toHaveBeenCalled(); }); it('should not call "loadInstanceSpy" when instance is not resolved', async () => { @@ -246,15 +240,15 @@ describe('Injector', () => { collection as any, null!, ); - expect(loadInstanceSpy.called).to.be.false; + expect(loadInstanceSpy).not.toHaveBeenCalled(); }); }); describe('loadController', () => { - let loadInstance: sinon.SinonSpy; + let loadInstance: ReturnType; beforeEach(() => { - loadInstance = sinon.spy(); + loadInstance = vi.fn(); injector.loadInstance = loadInstance; }); @@ -263,16 +257,21 @@ describe('Injector', () => { const wrapper = { test: 'test', getEnhancersMetadata: () => [] }; await injector.loadController(wrapper as any, module as any); - expect(loadInstance.calledWith(wrapper, module.controllers, module)).to.be - .true; + expect(loadInstance).toHaveBeenCalledWith( + wrapper, + module.controllers, + module, + expect.anything(), + wrapper, + ); }); }); describe('loadInjectable', () => { - let loadInstance: sinon.SinonSpy; + let loadInstance: ReturnType; beforeEach(() => { - loadInstance = sinon.spy(); + loadInstance = vi.fn(); injector.loadInstance = loadInstance; }); @@ -281,13 +280,18 @@ describe('Injector', () => { const wrapper = { test: 'test' }; await injector.loadInjectable(wrapper as any, module as any); - expect(loadInstance.calledWith(wrapper, module.injectables, module)).to.be - .true; + expect(loadInstance).toHaveBeenCalledWith( + wrapper, + module.injectables, + module, + expect.anything(), + undefined, + ); }); }); describe('lookupComponent', () => { - let lookupComponentInImports: sinon.SinonStub; + let lookupComponentInImports: ReturnType; const metatype = { name: 'test', metatype: { name: 'test' } }; const wrapper = new InstanceWrapper({ name: 'Test', @@ -296,7 +300,7 @@ describe('Injector', () => { isResolved: false, }); beforeEach(() => { - lookupComponentInImports = sinon.stub(); + lookupComponentInImports = vi.fn(); (injector as any).lookupComponentInImports = lookupComponentInImports; }); @@ -312,10 +316,10 @@ describe('Injector', () => { { name: metatype.name, index: 0, dependencies: [] }, wrapper, ); - expect(result).to.be.equal(instance); + expect(result).toBe(instance); }); - it('should throw an exception if recursion happens', () => { + it('should throw an exception if recursion happens', async () => { const name = 'RecursionService'; const instance = { test: 3 }; const collection = { @@ -330,11 +334,11 @@ describe('Injector', () => { name, }), ); - expect(result).to.eventually.be.rejected; + await expect(result).rejects.toBeDefined(); }); it('should call "lookupComponentInImports" when object is not in collection', async () => { - lookupComponentInImports.returns({}); + lookupComponentInImports.mockReturnValue({}); const collection = { has: () => false, }; @@ -344,51 +348,51 @@ describe('Injector', () => { { name: metatype.name, index: 0, dependencies: [] }, wrapper, ); - expect(lookupComponentInImports.called).to.be.true; + expect(lookupComponentInImports).toHaveBeenCalled(); }); - it('should throw "UnknownDependenciesException" when instanceWrapper is null and "exports" collection does not contain token', () => { - lookupComponentInImports.returns(null); + it('should throw "UnknownDependenciesException" when instanceWrapper is null and "exports" collection does not contain token', async () => { + lookupComponentInImports.mockReturnValue(null); const collection = { has: () => false, }; const module = { exports: collection }; - expect( + await expect( injector.lookupComponent( collection as any, module as any, { name: metatype.name, index: 0, dependencies: [] }, wrapper, ), - ).to.eventually.be.rejected; + ).rejects.toBeDefined(); }); - it('should not throw "UnknownDependenciesException" instanceWrapper is not null', () => { - lookupComponentInImports.returns({}); + it('should not throw "UnknownDependenciesException" instanceWrapper is not null', async () => { + lookupComponentInImports.mockReturnValue({}); const collection = { has: () => false, }; const module = { exports: collection }; - expect( + await expect( injector.lookupComponent( collection as any, module as any, { name: metatype.name, index: 0, dependencies: [] }, wrapper, ), - ).to.eventually.be.not.rejected; + ).resolves.not.toThrow(); }); }); describe('lookupComponentInImports', () => { - let loadProvider: sinon.SinonSpy; + let loadProvider: ReturnType; const metatype = { name: 'test' }; const module = { relatedModules: new Map(), }; beforeEach(() => { - loadProvider = sinon.spy(); + loadProvider = vi.fn(); (injector as any).loadProvider = loadProvider; }); @@ -398,10 +402,10 @@ describe('Injector', () => { 'testToken', new InstanceWrapper(), ); - expect(result).to.be.eq(null); + expect(result).toBe(null); }); - it('should return null when related modules do not have appropriate component', () => { + it('should return null when related modules do not have appropriate component', async () => { let moduleFixture = { relatedModules: new Map([ [ @@ -417,13 +421,13 @@ describe('Injector', () => { ], ] as any), }; - expect( + await expect( injector.lookupComponentInImports( moduleFixture as any, metatype as any, null!, ), - ).to.be.eventually.eq(null); + ).resolves.toBe(null); moduleFixture = { relatedModules: new Map([ @@ -440,13 +444,13 @@ describe('Injector', () => { ], ] as any), }; - expect( + await expect( injector.lookupComponentInImports( moduleFixture as any, metatype as any, null!, ), - ).to.eventually.be.eq(null); + ).resolves.toBe(null); }); }); @@ -464,13 +468,11 @@ describe('Injector', () => { }; }); it('return forwardRef() result', () => { - expect(injector.resolveParamToken(wrapper, param)).to.be.eql( - forwardRef, - ); + expect(injector.resolveParamToken(wrapper, param)).toEqual(forwardRef); }); it('set wrapper "forwardRef" property to true', () => { injector.resolveParamToken(wrapper, param); - expect(wrapper.forwardRef).to.be.true; + expect(wrapper.forwardRef).toBe(true); }); }); describe('when "forwardRef" property is nil', () => { @@ -481,10 +483,10 @@ describe('Injector', () => { }); it('set wrapper "forwardRef" property to false', () => { injector.resolveParamToken(wrapper, param); - expect(wrapper.forwardRef).to.be.undefined; + expect(wrapper.forwardRef).toBeUndefined(); }); it('return param', () => { - expect(injector.resolveParamToken(wrapper, param)).to.be.eql(param); + expect(injector.resolveParamToken(wrapper, param)).toEqual(param); }); }); }); @@ -501,39 +503,39 @@ describe('Injector', () => { it('should call loadProvider', async () => { const wrapper = new InstanceWrapper({ isResolved: false }); - const loadStub = sinon - .stub(injector, 'loadProvider') - .callsFake(() => null!); + const loadStub = vi + .spyOn(injector, 'loadProvider') + .mockImplementation(() => null!); await injector.resolveComponentHost(module, wrapper); - expect(loadStub.called).to.be.true; + expect(loadStub).toHaveBeenCalled(); }); it('should not call loadProvider (isResolved)', async () => { const wrapper = new InstanceWrapper({ isResolved: true }); - const loadStub = sinon - .stub(injector, 'loadProvider') - .callsFake(() => null!); + const loadStub = vi + .spyOn(injector, 'loadProvider') + .mockImplementation(() => null!); await injector.resolveComponentHost(module, wrapper); - expect(loadStub.called).to.be.false; + expect(loadStub).not.toHaveBeenCalled(); }); it('should not call loadProvider (forwardRef)', async () => { const wrapper = new InstanceWrapper({ isResolved: false, forwardRef: true, }); - const loadStub = sinon - .stub(injector, 'loadProvider') - .callsFake(() => null!); + const loadStub = vi + .spyOn(injector, 'loadProvider') + .mockImplementation(() => null!); await injector.resolveComponentHost(module, wrapper); - expect(loadStub.called).to.be.false; + expect(loadStub).not.toHaveBeenCalled(); }); }); describe('when instanceWrapper has async property', () => { it('should await instance', async () => { - sinon.stub(injector, 'loadProvider').callsFake(() => null!); + vi.spyOn(injector, 'loadProvider').mockImplementation(() => null!); const instance = Promise.resolve(true); const wrapper = new InstanceWrapper({ @@ -544,7 +546,7 @@ describe('Injector', () => { }); const result = await injector.resolveComponentHost(module, wrapper); - expect(result.instance).to.be.true; + expect(result.instance).toBe(true); }); }); @@ -552,7 +554,7 @@ describe('Injector', () => { it('should call settlementSignal.error when loadProvider throws', async () => { const error = new Error('Test error'); const settlementSignal = new SettlementSignal(); - const errorSpy = sinon.spy(settlementSignal, 'error'); + const errorSpy = vi.spyOn(settlementSignal, 'error'); const wrapper = new InstanceWrapper({ isResolved: false, @@ -564,15 +566,15 @@ describe('Injector', () => { const instanceHost = wrapper.getInstanceByContextId(contextId); instanceHost.donePromise = Promise.resolve(); - sinon - .stub(injector, 'loadProvider') - .callsFake(() => Promise.reject(error)); + vi.spyOn(injector, 'loadProvider').mockImplementation(() => + Promise.reject(error), + ); await injector.resolveComponentHost(module, wrapper, contextId); await new Promise(resolve => setImmediate(resolve)); - expect(errorSpy.calledOnce).to.be.true; - expect(errorSpy.calledWith(error)).to.be.true; + expect(errorSpy).toHaveBeenCalledOnce(); + expect(errorSpy).toHaveBeenCalledWith(error); }); }); }); @@ -580,7 +582,7 @@ describe('Injector', () => { describe('applyProperties', () => { describe('when instance is not an object', () => { it('should return undefined', () => { - expect(injector.applyProperties('test', [])).to.be.undefined; + expect(injector.applyProperties('test', [])).toBeUndefined(); }); }); @@ -594,9 +596,9 @@ describe('Injector', () => { const obj: Record = {}; injector.applyProperties(obj, properties as PropertyDependency[]); - expect(obj.one).to.be.eql(properties[0].instance); - expect(obj.two).to.be.undefined; - expect(obj.three).to.be.eql(properties[2].instance); + expect(obj.one).toEqual(properties[0].instance); + expect(obj.two).toBeUndefined(); + expect(obj.three).toEqual(properties[2].instance); }); }); }); @@ -609,8 +611,8 @@ describe('Injector', () => { const wrapper = new InstanceWrapper({ metatype: TestClass }); await injector.instantiateClass([], wrapper, wrapper, STATIC_CONTEXT); - expect(wrapper.instance).to.not.be.undefined; - expect(wrapper.instance).to.be.instanceOf(TestClass); + expect(wrapper.instance).not.toBeUndefined(); + expect(wrapper.instance).toBeInstanceOf(TestClass); }); it('should call factory', async () => { const wrapper = new InstanceWrapper({ @@ -619,7 +621,7 @@ describe('Injector', () => { }); await injector.instantiateClass([], wrapper, wrapper, STATIC_CONTEXT); - expect(wrapper.instance).to.not.be.undefined; + expect(wrapper.instance).not.toBeUndefined(); }); }); describe('when context is not static', () => { @@ -628,18 +630,18 @@ describe('Injector', () => { const wrapper = new InstanceWrapper({ metatype: TestClass }); await injector.instantiateClass([], wrapper, wrapper, ctx); - expect(wrapper.instance).to.be.undefined; - expect(wrapper.getInstanceByContextId(ctx).isResolved).to.be.true; + expect(wrapper.instance).toBeUndefined(); + expect(wrapper.getInstanceByContextId(ctx).isResolved).toBe(true); }); it('should not call factory', async () => { const wrapper = new InstanceWrapper({ inject: [], - metatype: sinon.spy() as any, + metatype: vi.fn() as any, }); await injector.instantiateClass([], wrapper, wrapper, { id: 2 }); - expect(wrapper.instance).to.be.undefined; - expect((wrapper.metatype as any).called).to.be.false; + expect(wrapper.instance).toBeUndefined(); + expect(wrapper.metatype as any).not.toHaveBeenCalled(); }); }); }); @@ -664,7 +666,7 @@ describe('Injector', () => { moduleRef.providers, ctx, ); - expect(instance).to.be.instanceOf(TestClass); + expect(instance).toBeInstanceOf(TestClass); }); }); @@ -682,12 +684,12 @@ describe('Injector', () => { }), ); - const loadInstanceStub = sinon - .stub(injector, 'loadInstance') - .callsFake(async () => ({}) as any); + const loadInstanceStub = vi + .spyOn(injector, 'loadInstance') + .mockImplementation(async () => ({}) as any); await injector.loadEnhancersPerContext(wrapper, STATIC_CONTEXT); - expect(loadInstanceStub.calledTwice).to.be.true; + expect(loadInstanceStub).toHaveBeenCalledTimes(2); }); }); @@ -697,15 +699,15 @@ describe('Injector', () => { wrapper.addCtorMetadata(0, new InstanceWrapper()); wrapper.addCtorMetadata(1, new InstanceWrapper()); - const resolveComponentHostStub = sinon - .stub(injector, 'resolveComponentHost') - .callsFake(async () => new InstanceWrapper()); + const resolveComponentHostStub = vi + .spyOn(injector, 'resolveComponentHost') + .mockImplementation(async () => new InstanceWrapper()); await injector.loadCtorMetadata( wrapper.getCtorMetadata(), STATIC_CONTEXT, ); - expect(resolveComponentHostStub.calledTwice).to.be.true; + expect(resolveComponentHostStub).toHaveBeenCalledTimes(2); }); }); @@ -715,15 +717,15 @@ describe('Injector', () => { wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); wrapper.addPropertiesMetadata('key2', new InstanceWrapper()); - const resolveComponentHostStub = sinon - .stub(injector, 'resolveComponentHost') - .callsFake(async () => new InstanceWrapper()); + const resolveComponentHostStub = vi + .spyOn(injector, 'resolveComponentHost') + .mockImplementation(async () => new InstanceWrapper()); await injector.loadPropertiesMetadata( wrapper.getPropertiesMetadata(), STATIC_CONTEXT, ); - expect(resolveComponentHostStub.calledTwice).to.be.true; + expect(resolveComponentHostStub).toHaveBeenCalledTimes(2); }); }); @@ -731,15 +733,15 @@ describe('Injector', () => { it('should call "loadCtorMetadata" if metadata is not undefined', async () => { const wrapper = new InstanceWrapper(); const metadata = []; - sinon.stub(wrapper, 'getCtorMetadata').callsFake(() => metadata); + vi.spyOn(wrapper, 'getCtorMetadata').mockImplementation(() => metadata); - const loadCtorMetadataSpy = sinon.spy(injector, 'loadCtorMetadata'); + const loadCtorMetadataSpy = vi.spyOn(injector, 'loadCtorMetadata'); await injector.resolveConstructorParams( wrapper, null!, [], () => { - expect(loadCtorMetadataSpy.called).to.be.true; + expect(loadCtorMetadataSpy).toHaveBeenCalled(); }, { id: 2 }, ); @@ -750,14 +752,16 @@ describe('Injector', () => { it('should call "loadPropertiesMetadata" if metadata is not undefined', async () => { const wrapper = new InstanceWrapper(); const metadata = []; - sinon.stub(wrapper, 'getPropertiesMetadata').callsFake(() => metadata); + vi.spyOn(wrapper, 'getPropertiesMetadata').mockImplementation( + () => metadata, + ); - const loadPropertiesMetadataSpy = sinon.spy( + const loadPropertiesMetadataSpy = vi.spyOn( injector, 'loadPropertiesMetadata', ); await injector.resolveProperties(wrapper, null!, null!, { id: 2 }); - expect(loadPropertiesMetadataSpy.called).to.be.true; + expect(loadPropertiesMetadataSpy).toHaveBeenCalled(); }); }); @@ -778,8 +782,8 @@ describe('Injector', () => { const [dependencies, optionalDependenciesIds] = injector.getClassDependencies(wrapper); - expect(dependencies).to.deep.eq([FixtureDep1, FixtureDep2]); - expect(optionalDependenciesIds).to.deep.eq([1]); + expect(dependencies).toEqual([FixtureDep1, FixtureDep2]); + expect(optionalDependenciesIds).toEqual([1]); }); it('should not mutate the constructor metadata', async () => { @@ -794,10 +798,10 @@ describe('Injector', () => { const wrapper = new InstanceWrapper({ metatype: FixtureClass }); const [dependencies] = injector.getClassDependencies(wrapper); - expect(dependencies).to.deep.eq([injectionToken]); + expect(dependencies).toEqual([injectionToken]); const paramtypes = Reflect.getMetadata(PARAMTYPES_METADATA, FixtureClass); - expect(paramtypes).to.deep.eq([FixtureDep1]); + expect(paramtypes).toEqual([FixtureDep1]); }); }); @@ -817,13 +821,8 @@ describe('Injector', () => { const [dependencies, optionalDependenciesIds] = injector.getFactoryProviderDependencies(wrapper); - expect(dependencies).to.deep.eq([ - FixtureDep1, - FixtureDep2, - FixtureDep2, - {}, - ]); - expect(optionalDependenciesIds).to.deep.eq([1]); + expect(dependencies).toEqual([FixtureDep1, FixtureDep2, FixtureDep2, {}]); + expect(optionalDependenciesIds).toEqual([1]); }); }); @@ -847,7 +846,7 @@ describe('Injector', () => { }); it('should add dependency metadata to PropertiesMetadata when key is symbol', async () => { - const addPropertiesMetadataSpy = sinon.spy( + const addPropertiesMetadataSpy = vi.spyOn( hostWrapper, 'addPropertiesMetadata', ); @@ -855,11 +854,11 @@ describe('Injector', () => { const key = Symbol.for('symbol'); exposedInjector.addDependencyMetadata(key, hostWrapper, instanceWrapper); - expect(addPropertiesMetadataSpy.called).to.be.true; + expect(addPropertiesMetadataSpy).toHaveBeenCalled(); }); it('should add dependency metadata to PropertiesMetadata when key is string', async () => { - const addPropertiesMetadataSpy = sinon.spy( + const addPropertiesMetadataSpy = vi.spyOn( hostWrapper, 'addPropertiesMetadata', ); @@ -867,16 +866,16 @@ describe('Injector', () => { const key = 'string'; exposedInjector.addDependencyMetadata(key, hostWrapper, instanceWrapper); - expect(addPropertiesMetadataSpy.called).to.be.true; + expect(addPropertiesMetadataSpy).toHaveBeenCalled(); }); it('should add dependency metadata to CtorMetadata when key is number', async () => { - const addCtorMetadataSpy = sinon.spy(hostWrapper, 'addCtorMetadata'); + const addCtorMetadataSpy = vi.spyOn(hostWrapper, 'addCtorMetadata'); const key = 0; exposedInjector.addDependencyMetadata(key, hostWrapper, instanceWrapper); - expect(addCtorMetadataSpy.called).to.be.true; + expect(addCtorMetadataSpy).toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/injector/instance-loader.spec.ts b/packages/core/test/injector/instance-loader.spec.ts index 5ee507cdf2a..be70d7978ba 100644 --- a/packages/core/test/injector/instance-loader.spec.ts +++ b/packages/core/test/injector/instance-loader.spec.ts @@ -1,12 +1,10 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Injectable } from '../../../common'; -import { Controller } from '../../../common/decorators/core/controller.decorator'; -import { NestContainer } from '../../injector/container'; -import { Injector } from '../../injector/injector'; -import { InstanceLoader } from '../../injector/instance-loader'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { GraphInspector } from '../../inspector/graph-inspector'; +import { Controller } from '../../../common/decorators/core/controller.decorator.js'; +import { Injectable } from '../../../common/index.js'; +import { NestContainer } from '../../injector/container.js'; +import { Injector } from '../../injector/injector.js'; +import { InstanceLoader } from '../../injector/instance-loader.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { GraphInspector } from '../../inspector/graph-inspector.js'; describe('InstanceLoader', () => { @Controller('') @@ -19,22 +17,20 @@ describe('InstanceLoader', () => { let injector: Injector; let container: NestContainer; let graphInspector: GraphInspector; - let inspectInstanceWrapperStub: sinon.SinonStub; - let mockContainer: sinon.SinonMock; + let inspectInstanceWrapperStub: ReturnType; let moduleMock: Record; beforeEach(() => { container = new NestContainer(); graphInspector = new GraphInspector(container); - inspectInstanceWrapperStub = sinon.stub( + inspectInstanceWrapperStub = vi.spyOn( graphInspector, 'inspectInstanceWrapper', ); injector = new Injector(); loader = new InstanceLoader(container, injector, graphInspector); - mockContainer = sinon.mock(container); moduleMock = { imports: new Set(), @@ -47,7 +43,7 @@ describe('InstanceLoader', () => { const modules = new Map(); modules.set('Test', moduleMock); - mockContainer.expects('getModules').returns(modules); + vi.spyOn(container, 'getModules').mockReturnValue(modules); }); it('should call "loadPrototype" for every provider and controller in every module', async () => { @@ -65,28 +61,29 @@ describe('InstanceLoader', () => { moduleMock.providers.set('TestProvider', providerWrapper); moduleMock.controllers.set('TestRoute', ctrlWrapper); - const loadProviderPrototypeStub = sinon.stub(injector, 'loadPrototype'); + const loadProviderPrototypeStub = vi + .spyOn(injector, 'loadPrototype') + .mockImplementation(() => ({}) as any); - sinon.stub(injector, 'loadController'); - sinon.stub(injector, 'loadProvider'); + vi.spyOn(injector, 'loadController').mockImplementation(() => ({}) as any); + vi.spyOn(injector, 'loadProvider').mockImplementation(() => ({}) as any); await loader.createInstancesOfDependencies(); - expect( - loadProviderPrototypeStub.calledWith( - providerWrapper, - moduleMock.providers, - ), - ).to.be.true; - expect( - loadProviderPrototypeStub.calledWith(ctrlWrapper, moduleMock.controllers), - ).to.be.true; + expect(loadProviderPrototypeStub).toHaveBeenCalledWith( + providerWrapper, + moduleMock.providers, + ); + expect(loadProviderPrototypeStub).toHaveBeenCalledWith( + ctrlWrapper, + moduleMock.controllers, + ); }); describe('for every provider in every module', () => { const testProviderToken = 'TestProvider'; - let loadProviderStub: sinon.SinonStub; + let loadProviderStub: ReturnType; beforeEach(async () => { const testProviderWrapper = new InstanceWrapper({ @@ -97,33 +94,33 @@ describe('InstanceLoader', () => { }); moduleMock.providers.set(testProviderToken, testProviderWrapper); - loadProviderStub = sinon.stub(injector, 'loadProvider'); - sinon.stub(injector, 'loadController'); + loadProviderStub = vi + .spyOn(injector, 'loadProvider') + .mockImplementation(() => ({}) as any); + vi.spyOn(injector, 'loadController').mockImplementation( + () => ({}) as any, + ); await loader.createInstancesOfDependencies(); }); it('should call "loadProvider"', async () => { - expect( - loadProviderStub.calledWith( - moduleMock.providers.get(testProviderToken), - moduleMock as any, - ), - ).to.be.true; + expect(loadProviderStub).toHaveBeenCalledWith( + moduleMock.providers.get(testProviderToken), + moduleMock as any, + ); }); it('should call "inspectInstanceWrapper"', async () => { - expect( - inspectInstanceWrapperStub.calledWith( - moduleMock.providers.get(testProviderToken), - moduleMock as any, - ), - ).to.be.true; + expect(inspectInstanceWrapperStub).toHaveBeenCalledWith( + moduleMock.providers.get(testProviderToken), + moduleMock as any, + ); }); }); describe('for every controller in every module', () => { - let loadControllerStub: sinon.SinonStub; + let loadControllerStub: ReturnType; beforeEach(async () => { const wrapper = new InstanceWrapper({ @@ -134,31 +131,29 @@ describe('InstanceLoader', () => { }); moduleMock.controllers.set('TestRoute', wrapper); - sinon.stub(injector, 'loadProvider'); - loadControllerStub = sinon.stub(injector, 'loadController'); + vi.spyOn(injector, 'loadProvider').mockImplementation(() => ({}) as any); + loadControllerStub = vi + .spyOn(injector, 'loadController') + .mockImplementation(() => ({}) as any); await loader.createInstancesOfDependencies(); }); it('should call "loadController"', async () => { - expect( - loadControllerStub.calledWith( - moduleMock.controllers.get('TestRoute'), - moduleMock as any, - ), - ).to.be.true; + expect(loadControllerStub).toHaveBeenCalledWith( + moduleMock.controllers.get('TestRoute'), + moduleMock as any, + ); }); it('should call "inspectInstanceWrapper"', async () => { - expect( - inspectInstanceWrapperStub.calledWith( - moduleMock.controllers.get('TestRoute'), - moduleMock as any, - ), - ).to.be.true; + expect(inspectInstanceWrapperStub).toHaveBeenCalledWith( + moduleMock.controllers.get('TestRoute'), + moduleMock as any, + ); }); }); describe('for every injectable in every module', () => { - let loadInjectableStub: sinon.SinonStub; + let loadInjectableStub: ReturnType; beforeEach(async () => { const testInjectable = new InstanceWrapper({ @@ -169,27 +164,27 @@ describe('InstanceLoader', () => { }); moduleMock.injectables.set('TestProvider', testInjectable); - loadInjectableStub = sinon.stub(injector, 'loadInjectable'); - sinon.stub(injector, 'loadController'); + loadInjectableStub = vi + .spyOn(injector, 'loadInjectable') + .mockImplementation(() => ({}) as any); + vi.spyOn(injector, 'loadController').mockImplementation( + () => ({}) as any, + ); await loader.createInstancesOfDependencies(); }); it('should call "loadInjectable"', async () => { - expect( - loadInjectableStub.calledWith( - moduleMock.injectables.get('TestProvider'), - moduleMock as any, - ), - ).to.be.true; + expect(loadInjectableStub).toHaveBeenCalledWith( + moduleMock.injectables.get('TestProvider'), + moduleMock as any, + ); }); it('should call "inspectInstanceWrapper"', async () => { - expect( - inspectInstanceWrapperStub.calledWith( - moduleMock.injectables.get('TestProvider'), - moduleMock as any, - ), - ).to.be.true; + expect(inspectInstanceWrapperStub).toHaveBeenCalledWith( + moduleMock.injectables.get('TestProvider'), + moduleMock as any, + ); }); }); }); diff --git a/packages/core/test/injector/instance-wrapper.spec.ts b/packages/core/test/injector/instance-wrapper.spec.ts index 1f986d1978e..85baa0615fc 100644 --- a/packages/core/test/injector/instance-wrapper.spec.ts +++ b/packages/core/test/injector/instance-wrapper.spec.ts @@ -1,9 +1,7 @@ import { Scope } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { createContextId } from '../../helpers'; -import { STATIC_CONTEXT } from '../../injector/constants'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; +import { createContextId } from '../../helpers/index.js'; +import { STATIC_CONTEXT } from '../../injector/constants.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; class TestClass {} @@ -18,16 +16,16 @@ describe('InstanceWrapper', () => { it('should assign partial', () => { const instance = new InstanceWrapper(partial); - expect(instance.name).to.be.eql(partial.name); - expect(instance.scope).to.be.eql(partial.scope); - expect(instance.metatype).to.be.eql(partial.metatype); + expect(instance.name).toEqual(partial.name); + expect(instance.scope).toEqual(partial.scope); + expect(instance.metatype).toEqual(partial.metatype); }); it('should set instance by context id', () => { const instance = new InstanceWrapper(partial); - expect( - instance.getInstanceByContextId(STATIC_CONTEXT).instance, - ).to.be.eql(partial.instance); + expect(instance.getInstanceByContextId(STATIC_CONTEXT).instance).toEqual( + partial.instance, + ); }); }); @@ -38,8 +36,8 @@ describe('InstanceWrapper', () => { const otherWrapper = new InstanceWrapper(); wrapper.addCtorMetadata(0, otherWrapper); otherWrapper.addCtorMetadata(0, wrapper); - expect(wrapper.isDependencyTreeStatic()).to.be.true; - expect(otherWrapper.isDependencyTreeStatic()).to.be.true; + expect(wrapper.isDependencyTreeStatic()).toBe(true); + expect(otherWrapper.isDependencyTreeStatic()).toBe(true); }); }); describe('when circular reference and one non static', () => { @@ -48,8 +46,8 @@ describe('InstanceWrapper', () => { const otherWrapper = new InstanceWrapper({ scope: Scope.REQUEST }); wrapper.addCtorMetadata(0, otherWrapper); otherWrapper.addCtorMetadata(0, wrapper); - expect(wrapper.isDependencyTreeStatic()).to.be.false; - expect(otherWrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); + expect(otherWrapper.isDependencyTreeStatic()).toBe(false); }); }); describe('when circular reference and one durable', () => { @@ -61,8 +59,8 @@ describe('InstanceWrapper', () => { }); wrapper.addCtorMetadata(0, otherWrapper); otherWrapper.addCtorMetadata(0, wrapper); - expect(wrapper.isDependencyTreeStatic()).to.be.false; - expect(otherWrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); + expect(otherWrapper.isDependencyTreeStatic()).toBe(false); }); }); describe('when request scoped', () => { @@ -70,7 +68,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.REQUEST, }); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); describe('when request scoped durable', () => { @@ -79,7 +77,7 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, durable: true, }); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); describe('when request scoped explicit non durable', () => { @@ -88,13 +86,13 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, durable: false, }); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); describe('when default', () => { it('should return true', () => { const wrapper = new InstanceWrapper({}); - expect(wrapper.isDependencyTreeStatic()).to.be.true; + expect(wrapper.isDependencyTreeStatic()).toBe(true); }); }); describe('when statically scoped', () => { @@ -108,7 +106,7 @@ describe('InstanceWrapper', () => { ); wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); describe('dependencies static, properties non static, enhancers static', () => { @@ -120,7 +118,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST }), ); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); describe('dependencies static, properties static, enhancers non static', () => { @@ -131,7 +129,7 @@ describe('InstanceWrapper', () => { wrapper.addEnhancerMetadata( new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); }); @@ -140,7 +138,7 @@ describe('InstanceWrapper', () => { it('should return true', () => { const wrapper = new InstanceWrapper(); wrapper.addCtorMetadata(0, new InstanceWrapper()); - expect(wrapper.isDependencyTreeStatic()).to.be.true; + expect(wrapper.isDependencyTreeStatic()).toBe(true); }); }); describe('when one is not static', () => { @@ -153,7 +151,7 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, }), ); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); }); @@ -163,7 +161,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper(); wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); wrapper.addPropertiesMetadata('key2', new InstanceWrapper()); - expect(wrapper.isDependencyTreeStatic()).to.be.true; + expect(wrapper.isDependencyTreeStatic()).toBe(true); }); }); describe('when one is not static', () => { @@ -174,7 +172,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST }), ); wrapper.addPropertiesMetadata('key2', new InstanceWrapper()); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); }); @@ -184,7 +182,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper(); wrapper.addEnhancerMetadata(new InstanceWrapper()); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeStatic()).to.be.true; + expect(wrapper.isDependencyTreeStatic()).toBe(true); }); }); describe('when one is not static', () => { @@ -194,7 +192,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST }), ); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeStatic()).to.be.false; + expect(wrapper.isDependencyTreeStatic()).toBe(false); }); }); }); @@ -208,8 +206,8 @@ describe('InstanceWrapper', () => { const otherWrapper = new InstanceWrapper(); wrapper.addCtorMetadata(0, otherWrapper); otherWrapper.addCtorMetadata(0, wrapper); - expect(wrapper.isDependencyTreeDurable()).to.be.false; - expect(otherWrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); + expect(otherWrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when circular reference and one non durable', () => { @@ -218,8 +216,8 @@ describe('InstanceWrapper', () => { const otherWrapper = new InstanceWrapper({ scope: Scope.REQUEST }); wrapper.addCtorMetadata(0, otherWrapper); otherWrapper.addCtorMetadata(0, wrapper); - expect(wrapper.isDependencyTreeDurable()).to.be.false; - expect(otherWrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); + expect(otherWrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when circular reference and one durable', () => { @@ -231,8 +229,8 @@ describe('InstanceWrapper', () => { }); wrapper.addCtorMetadata(0, otherWrapper); otherWrapper.addCtorMetadata(0, wrapper); - expect(wrapper.isDependencyTreeDurable()).to.be.true; - expect(otherWrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); + expect(otherWrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when request scoped and durable', () => { @@ -241,7 +239,7 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, durable: true, }); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when request scoped and non durable', () => { @@ -249,7 +247,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.REQUEST, }); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when request scoped and explicit non durable', () => { @@ -258,13 +256,13 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, durable: false, }); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when default scope', () => { it('should return false', () => { const wrapper = new InstanceWrapper(); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when statically scoped', () => { @@ -279,7 +277,7 @@ describe('InstanceWrapper', () => { wrapper.addCtorMetadata(1, new InstanceWrapper()); wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('dependencies non durable, properties durable, enhancers durable', () => { @@ -295,7 +293,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST }), ); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('dependencies non durable, properties durable', () => { @@ -311,7 +309,7 @@ describe('InstanceWrapper', () => { 'key2', new InstanceWrapper({ scope: Scope.REQUEST, durable: true }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('properties durable, enhancers non durable', () => { @@ -325,7 +323,7 @@ describe('InstanceWrapper', () => { wrapper.addEnhancerMetadata( new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('dependencies durable, enhancers non durable', () => { @@ -338,7 +336,7 @@ describe('InstanceWrapper', () => { wrapper.addEnhancerMetadata( new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); }); @@ -347,7 +345,7 @@ describe('InstanceWrapper', () => { it('should return false', () => { const wrapper = new InstanceWrapper({ scope: Scope.REQUEST }); wrapper.addCtorMetadata(0, new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when wrapper is durable and dependency is static', () => { @@ -357,7 +355,7 @@ describe('InstanceWrapper', () => { durable: true, }); wrapper.addCtorMetadata(0, new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when wrapper is non durable and dependency is durable', () => { @@ -369,7 +367,7 @@ describe('InstanceWrapper', () => { 0, new InstanceWrapper({ scope: Scope.REQUEST, durable: true }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when wrapper is durable and dependency is static', () => { @@ -379,7 +377,7 @@ describe('InstanceWrapper', () => { durable: true, }); wrapper.addCtorMetadata(0, new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when wrapper is durable and dependency is non durable', () => { @@ -392,14 +390,14 @@ describe('InstanceWrapper', () => { 0, new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when each is static', () => { it('should return false', () => { const wrapper = new InstanceWrapper(); wrapper.addCtorMetadata(0, new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when one is not static and non-durable', () => { @@ -412,7 +410,7 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when one is not static and durable', () => { @@ -426,7 +424,7 @@ describe('InstanceWrapper', () => { durable: true, }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when one is not static, durable and non durable', () => { @@ -446,7 +444,7 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); }); @@ -455,7 +453,7 @@ describe('InstanceWrapper', () => { it('should return false', () => { const wrapper = new InstanceWrapper({ scope: Scope.REQUEST }); wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when wrapper is durable and dependency is static', () => { @@ -465,7 +463,7 @@ describe('InstanceWrapper', () => { durable: true, }); wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when wrapper is non durable and dependency is durable', () => { @@ -477,7 +475,7 @@ describe('InstanceWrapper', () => { 'key1', new InstanceWrapper({ scope: Scope.REQUEST, durable: true }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when wrapper is durable and dependency is static', () => { @@ -487,7 +485,7 @@ describe('InstanceWrapper', () => { durable: true, }); wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when wrapper is durable and dependency is non durable', () => { @@ -500,7 +498,7 @@ describe('InstanceWrapper', () => { 'key1', new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when each is static', () => { @@ -508,7 +506,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper(); wrapper.addPropertiesMetadata('key1', new InstanceWrapper()); wrapper.addPropertiesMetadata('key2', new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when one is not static and non-durable', () => { @@ -519,7 +517,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST }), ); wrapper.addPropertiesMetadata('key2', new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when one is not static and durable', () => { @@ -530,7 +528,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST, durable: true }), ); wrapper.addPropertiesMetadata('key2', new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when one is not static, non durable and durable', () => { @@ -545,7 +543,7 @@ describe('InstanceWrapper', () => { 'key3', new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); }); @@ -554,7 +552,7 @@ describe('InstanceWrapper', () => { it('should return false', () => { const wrapper = new InstanceWrapper({ scope: Scope.REQUEST }); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when wrapper is durable and dependency is static', () => { @@ -564,7 +562,7 @@ describe('InstanceWrapper', () => { durable: true, }); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when wrapper is non durable and dependency is durable', () => { @@ -575,7 +573,7 @@ describe('InstanceWrapper', () => { wrapper.addEnhancerMetadata( new InstanceWrapper({ scope: Scope.REQUEST, durable: true }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when wrapper is durable and dependency is static', () => { @@ -585,7 +583,7 @@ describe('InstanceWrapper', () => { durable: true, }); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when wrapper is durable and dependency is non durable', () => { @@ -597,7 +595,7 @@ describe('InstanceWrapper', () => { wrapper.addEnhancerMetadata( new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when each is static', () => { @@ -605,7 +603,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper(); wrapper.addEnhancerMetadata(new InstanceWrapper()); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when one is not static and non-durable', () => { @@ -615,7 +613,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST }), ); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); describe('when one is not static and durable', () => { @@ -625,7 +623,7 @@ describe('InstanceWrapper', () => { new InstanceWrapper({ scope: Scope.REQUEST, durable: true }), ); wrapper.addEnhancerMetadata(new InstanceWrapper()); - expect(wrapper.isDependencyTreeDurable()).to.be.true; + expect(wrapper.isDependencyTreeDurable()).toBe(true); }); }); describe('when one is not static, non durable and durable', () => { @@ -638,7 +636,7 @@ describe('InstanceWrapper', () => { wrapper.addEnhancerMetadata( new InstanceWrapper({ scope: Scope.REQUEST }), ); - expect(wrapper.isDependencyTreeDurable()).to.be.false; + expect(wrapper.isDependencyTreeDurable()).toBe(false); }); }); }); @@ -649,13 +647,13 @@ describe('InstanceWrapper', () => { describe('when metatype is nil', () => { it('should return true', () => { const instance = new InstanceWrapper({ metatype: null }); - expect(instance.isNotMetatype).to.be.true; + expect(instance.isNotMetatype).toBe(true); }); }); describe('when metatype is not nil', () => { it('should return false', () => { const instance = new InstanceWrapper({ metatype: TestClass }); - expect(instance.isNotMetatype).to.be.false; + expect(instance.isNotMetatype).toBe(false); }); }); }); @@ -665,7 +663,7 @@ describe('InstanceWrapper', () => { const instance = new InstanceWrapper(); const enhancers = [new InstanceWrapper()]; instance.addEnhancerMetadata(enhancers[0]); - expect(instance.getEnhancersMetadata()).to.be.eql(enhancers); + expect(instance.getEnhancersMetadata()).toEqual(enhancers); }); }); @@ -675,7 +673,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper(); wrapper.instance = instance; - expect(wrapper.getInstanceByContextId(STATIC_CONTEXT).instance).to.be.eql( + expect(wrapper.getInstanceByContextId(STATIC_CONTEXT).instance).toEqual( instance, ); }); @@ -687,7 +685,7 @@ describe('InstanceWrapper', () => { const instance = { test: true }; const wrapper = new InstanceWrapper({ instance }); - expect(wrapper.cloneStaticInstance({ id: 0 }).instance).to.be.eql( + expect(wrapper.cloneStaticInstance({ id: 0 }).instance).toEqual( instance, ); }); @@ -697,7 +695,7 @@ describe('InstanceWrapper', () => { const instance = { test: true }; const wrapper = new InstanceWrapper({ instance, scope: Scope.REQUEST }); - expect(wrapper.cloneStaticInstance({ id: 0 }).instance).to.be.undefined; + expect(wrapper.cloneStaticInstance({ id: 0 }).instance).toBeUndefined(); }); }); }); @@ -708,12 +706,12 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.TRANSIENT, }); - const getInstanceByInquirerIdSpy = sinon.spy( + const getInstanceByInquirerIdSpy = vi.spyOn( wrapper, 'getInstanceByInquirerId', ); wrapper.getInstanceByContextId(STATIC_CONTEXT, 'inquirerId'); - expect(getInstanceByInquirerIdSpy.called).to.be.true; + expect(getInstanceByInquirerIdSpy).toHaveBeenCalled(); }); }); }); @@ -724,7 +722,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.TRANSIENT, }); - const setInstanceByInquirerIdSpy = sinon.spy( + const setInstanceByInquirerIdSpy = vi.spyOn( wrapper, 'setInstanceByInquirerId', ); @@ -733,7 +731,7 @@ describe('InstanceWrapper', () => { { instance: {} }, 'inquirerId', ); - expect(setInstanceByInquirerIdSpy.called).to.be.true; + expect(setInstanceByInquirerIdSpy).toHaveBeenCalled(); }); }); }); @@ -749,11 +747,11 @@ describe('InstanceWrapper', () => { wrapper.setInstanceByContextId(contextId, { instance: {} }); const existingContext = wrapper.getInstanceByContextId(contextId); - expect(existingContext.instance).to.be.not.undefined; + expect(existingContext.instance).toBeDefined(); wrapper.removeInstanceByContextId(contextId); const removedContext = wrapper.getInstanceByContextId(contextId); - expect(removedContext.instance).to.be.undefined; + expect(removedContext.instance).toBeUndefined(); }); }); @@ -773,14 +771,14 @@ describe('InstanceWrapper', () => { STATIC_CONTEXT, 'inquirerId', ); - expect(existingContext.instance).to.be.not.undefined; + expect(existingContext.instance).toBeDefined(); wrapper.removeInstanceByContextId(STATIC_CONTEXT, 'inquirerId'); const removedContext = wrapper.getInstanceByContextId( STATIC_CONTEXT, 'inquirerId', ); - expect(removedContext.instance).to.be.undefined; + expect(removedContext.instance).toBeUndefined(); }); }); }); @@ -791,7 +789,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.REQUEST, }); - expect(wrapper.isInRequestScope({ id: 3 })).to.be.true; + expect(wrapper.isInRequestScope({ id: 3 })).toBe(true); }); }); describe('otherwise', () => { @@ -799,12 +797,12 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.TRANSIENT, }); - expect(wrapper.isInRequestScope({ id: 3 })).to.be.false; + expect(wrapper.isInRequestScope({ id: 3 })).toBe(false); const wrapper2 = new InstanceWrapper({ scope: Scope.REQUEST, }); - expect(wrapper2.isInRequestScope(STATIC_CONTEXT)).to.be.false; + expect(wrapper2.isInRequestScope(STATIC_CONTEXT)).toBe(false); }); }); }); @@ -822,7 +820,7 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, }), ), - ).to.be.true; + ).toBe(true); }); }); describe('otherwise', () => { @@ -830,8 +828,9 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.TRANSIENT, }); - expect(wrapper.isLazyTransient({ id: 3 }, new InstanceWrapper())).to.be - .false; + expect(wrapper.isLazyTransient({ id: 3 }, new InstanceWrapper())).toBe( + false, + ); const wrapper2 = new InstanceWrapper({ scope: Scope.REQUEST, @@ -843,7 +842,7 @@ describe('InstanceWrapper', () => { scope: Scope.TRANSIENT, }), ), - ).to.be.false; + ).toBe(false); }); }); }); @@ -861,7 +860,7 @@ describe('InstanceWrapper', () => { scope: Scope.DEFAULT, }), ), - ).to.be.true; + ).toBe(true); }); }); describe('otherwise', () => { @@ -869,7 +868,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.REQUEST, }); - expect(wrapper.isStatic({ id: 3 }, new InstanceWrapper())).to.be.false; + expect(wrapper.isStatic({ id: 3 }, new InstanceWrapper())).toBe(false); const wrapper2 = new InstanceWrapper({ scope: Scope.TRANSIENT, @@ -881,7 +880,7 @@ describe('InstanceWrapper', () => { scope: Scope.REQUEST, }), ), - ).to.be.false; + ).toBe(false); }); }); }); @@ -892,7 +891,7 @@ describe('InstanceWrapper', () => { const wrapper = new InstanceWrapper({ scope: Scope.DEFAULT, }); - expect(wrapper.getStaticTransientInstances()).to.be.eql([]); + expect(wrapper.getStaticTransientInstances()).toEqual([]); }); }); describe('when instance is transient', () => { @@ -905,7 +904,7 @@ describe('InstanceWrapper', () => { isConstructorCalled: true, }; wrapper.setInstanceByInquirerId(STATIC_CONTEXT, 'test', instanceHost); - expect(wrapper.getStaticTransientInstances()).to.be.eql([instanceHost]); + expect(wrapper.getStaticTransientInstances()).toEqual([instanceHost]); }); describe('lifecycle hooks on transient services', () => { @@ -927,7 +926,7 @@ describe('InstanceWrapper', () => { ); // Should not include this instance for lifecycle hooks - expect(wrapper.getStaticTransientInstances()).to.be.eql([]); + expect(wrapper.getStaticTransientInstances()).toEqual([]); }); it('should include instances where constructor was actually invoked', () => { @@ -949,7 +948,7 @@ describe('InstanceWrapper', () => { ); // Should include this instance for lifecycle hooks - expect(wrapper.getStaticTransientInstances()).to.be.eql([ + expect(wrapper.getStaticTransientInstances()).toEqual([ properInstance, ]); }); @@ -966,9 +965,9 @@ describe('InstanceWrapper', () => { provide: 'token', }); - expect( - wrapper.getInstanceByContextId(STATIC_CONTEXT).instance, - ).to.be.equal('value'); + expect(wrapper.getInstanceByContextId(STATIC_CONTEXT).instance).toBe( + 'value', + ); }); }); @@ -981,7 +980,7 @@ describe('InstanceWrapper', () => { provide: 'token', }); - expect(wrapper.metatype).to.be.eql(TestClass); + expect(wrapper.metatype).toEqual(TestClass); }); }); @@ -999,8 +998,8 @@ describe('InstanceWrapper', () => { inject: injectedDependencies, }); - expect(wrapper.metatype).to.be.eql(factory); - expect(wrapper.inject).to.be.eq(injectedDependencies); + expect(wrapper.metatype).toEqual(factory); + expect(wrapper.inject).toBe(injectedDependencies); }); }); @@ -1014,10 +1013,173 @@ describe('InstanceWrapper', () => { useFactory: factory, }); - expect(wrapper.metatype).to.be.eql(factory); - expect(wrapper.inject).to.be.eql([]); + expect(wrapper.metatype).toEqual(factory); + expect(wrapper.inject).toEqual([]); }); }); }); }); + + describe('id', () => { + it('should return a string identifier', () => { + const wrapper = new InstanceWrapper({ name: 'TestId' }); + expect(typeof wrapper.id).toBe('string'); + expect(wrapper.id.length).toBeGreaterThan(0); + }); + }); + + describe('isFactory', () => { + it('should return true when inject is defined', () => { + const wrapper = new InstanceWrapper({ + metatype: TestClass, + inject: ['dep1'], + }); + expect(wrapper.isFactory).toBe(true); + }); + + it('should return false when inject is not defined', () => { + const wrapper = new InstanceWrapper({ metatype: TestClass }); + expect(wrapper.isFactory).toBe(false); + }); + }); + + describe('isTransient', () => { + it('should return true when scope is TRANSIENT', () => { + const wrapper = new InstanceWrapper({ scope: Scope.TRANSIENT }); + expect(wrapper.isTransient).toBe(true); + }); + + it('should return false when scope is DEFAULT', () => { + const wrapper = new InstanceWrapper({ scope: Scope.DEFAULT }); + expect(wrapper.isTransient).toBe(false); + }); + }); + + describe('isNotMetatype with inject set', () => { + it('should return true when metatype is set but inject is also set (factory)', () => { + const wrapper = new InstanceWrapper({ + metatype: (() => {}) as any, + inject: [], + }); + expect(wrapper.isNotMetatype).toBe(true); + }); + }); + + describe('createPrototype', () => { + it('should return prototype when metatype is newable and not resolved', () => { + const wrapper = new InstanceWrapper({ metatype: TestClass }); + const proto = wrapper.createPrototype(STATIC_CONTEXT); + expect(proto).toBeDefined(); + expect(Object.getPrototypeOf(proto)).toBe(TestClass.prototype); + }); + + it('should return undefined when instance is already resolved', () => { + const wrapper = new InstanceWrapper({ + metatype: TestClass, + instance: new TestClass(), + isResolved: true, + }); + const proto = wrapper.createPrototype(STATIC_CONTEXT); + expect(proto).toBeUndefined(); + }); + + it('should return undefined when inject is set (factory provider)', () => { + const wrapper = new InstanceWrapper({ + metatype: (() => {}) as any, + inject: [], + }); + const proto = wrapper.createPrototype(STATIC_CONTEXT); + expect(proto).toBeUndefined(); + }); + }); + + describe('isExplicitlyRequested', () => { + it('should return true when inquirer is self', () => { + const wrapper = new InstanceWrapper({ scope: Scope.DEFAULT }); + expect(wrapper.isExplicitlyRequested({ id: 3 }, wrapper)).toBe(true); + }); + + it('should return true when inquirer is transient', () => { + const wrapper = new InstanceWrapper({ scope: Scope.DEFAULT }); + const inquirer = new InstanceWrapper({ scope: Scope.TRANSIENT }); + expect(wrapper.isExplicitlyRequested({ id: 3 }, inquirer)).toBe(true); + }); + + it('should return false in static context', () => { + const wrapper = new InstanceWrapper({ scope: Scope.DEFAULT }); + expect(wrapper.isExplicitlyRequested(STATIC_CONTEXT)).toBe(false); + }); + }); + + describe('attachRootInquirer', () => { + it('should set rootInquirer for transient wrapper', () => { + const wrapper = new InstanceWrapper({ scope: Scope.TRANSIENT }); + const inquirer = new InstanceWrapper({ name: 'root' }); + wrapper.attachRootInquirer(inquirer); + expect(wrapper.getRootInquirer()).toBe(inquirer); + }); + + it('should not set rootInquirer for non-transient wrapper', () => { + const wrapper = new InstanceWrapper({ scope: Scope.DEFAULT }); + const inquirer = new InstanceWrapper({ name: 'root' }); + wrapper.attachRootInquirer(inquirer); + expect(wrapper.getRootInquirer()).toBeUndefined(); + }); + + it('should use root inquirer of inquirer when available', () => { + const root = new InstanceWrapper({ name: 'root', scope: Scope.DEFAULT }); + const mid = new InstanceWrapper({ name: 'mid', scope: Scope.TRANSIENT }); + mid.attachRootInquirer(root); + + const leaf = new InstanceWrapper({ + name: 'leaf', + scope: Scope.TRANSIENT, + }); + leaf.attachRootInquirer(mid); + expect(leaf.getRootInquirer()).toBe(root); + }); + }); + + describe('removeInstanceByInquirerId', () => { + it('should handle missing collection gracefully', () => { + const wrapper = new InstanceWrapper({ scope: Scope.TRANSIENT }); + expect(() => + wrapper.removeInstanceByInquirerId(STATIC_CONTEXT, 'nonexistent'), + ).not.toThrow(); + }); + }); + + describe('cloneTransientInstance', () => { + it('should create a new instance per context for transient scope', () => { + const wrapper = new InstanceWrapper({ + metatype: TestClass, + scope: Scope.TRANSIENT, + instance: new TestClass(), + }); + const contextId = createContextId(); + const clone = wrapper.cloneTransientInstance(contextId, 'inquirer'); + expect(clone.instance).toBeDefined(); + expect(clone.isResolved).toBe(false); + }); + }); + + describe('mergeWith edge cases', () => { + it('should reset metatype and inject for value provider', () => { + const wrapper = new InstanceWrapper({ + metatype: TestClass, + inject: ['dep'], + }); + wrapper.mergeWith({ provide: 'token', useValue: 42 }); + expect(wrapper.metatype).toBeNull(); + expect(wrapper.inject).toBeNull(); + expect(wrapper.scope).toBe(Scope.DEFAULT); + }); + + it('should reset inject for class provider', () => { + const wrapper = new InstanceWrapper({ inject: ['dep'] }); + wrapper.mergeWith({ provide: 'token', useClass: TestClass }); + expect(wrapper.inject).toBeNull(); + expect(wrapper.metatype).toBe(TestClass); + }); + }); }); diff --git a/packages/core/test/injector/internal-core-module/internal-core-module-factory.spec.ts b/packages/core/test/injector/internal-core-module/internal-core-module-factory.spec.ts index b5bfcd32ee3..84da19354ce 100644 --- a/packages/core/test/injector/internal-core-module/internal-core-module-factory.spec.ts +++ b/packages/core/test/injector/internal-core-module/internal-core-module-factory.spec.ts @@ -1,12 +1,11 @@ import { ClassProvider, FactoryProvider } from '@nestjs/common'; -import { expect } from 'chai'; -import { ExternalContextCreator } from '../../../helpers/external-context-creator'; -import { HttpAdapterHost } from '../../../helpers/http-adapter-host'; -import { LazyModuleLoader, ModulesContainer } from '../../../injector'; -import { NestContainer } from '../../../injector/container'; -import { InternalCoreModule } from '../../../injector/internal-core-module/internal-core-module'; -import { InternalCoreModuleFactory } from '../../../injector/internal-core-module/internal-core-module-factory'; -import { SerializedGraph } from '../../../inspector/serialized-graph'; +import { ExternalContextCreator } from '../../../helpers/external-context-creator.js'; +import { HttpAdapterHost } from '../../../helpers/http-adapter-host.js'; +import { LazyModuleLoader, ModulesContainer } from '../../../injector/index.js'; +import { NestContainer } from '../../../injector/container.js'; +import { InternalCoreModule } from '../../../injector/internal-core-module/internal-core-module.js'; +import { InternalCoreModuleFactory } from '../../../injector/internal-core-module/internal-core-module-factory.js'; +import { SerializedGraph } from '../../../inspector/serialized-graph.js'; describe('InternalCoreModuleFactory', () => { it('should return the internal core module definition', () => { @@ -18,12 +17,12 @@ describe('InternalCoreModuleFactory', () => { null!, ); - expect(moduleDefinition.module).to.equal(InternalCoreModule); + expect(moduleDefinition.module).toBe(InternalCoreModule); const providedInjectables = moduleDefinition.providers!.map( item => (item as ClassProvider | FactoryProvider).provide, ); - expect(providedInjectables).to.deep.equal([ + expect(providedInjectables).toEqual([ ExternalContextCreator, ModulesContainer, HttpAdapterHost, @@ -34,7 +33,7 @@ describe('InternalCoreModuleFactory', () => { const lazyModuleLoaderProvider = moduleDefinition.providers!.find( item => (item as FactoryProvider)?.provide === LazyModuleLoader, ) as FactoryProvider; - expect(lazyModuleLoaderProvider.useFactory()).to.be.instanceOf( + expect(lazyModuleLoaderProvider.useFactory()).toBeInstanceOf( LazyModuleLoader, ); }); diff --git a/packages/core/test/injector/lazy-module-loader/lazy-module-loader.spec.ts b/packages/core/test/injector/lazy-module-loader/lazy-module-loader.spec.ts index 4f2645272cd..5249047cc4f 100644 --- a/packages/core/test/injector/lazy-module-loader/lazy-module-loader.spec.ts +++ b/packages/core/test/injector/lazy-module-loader/lazy-module-loader.spec.ts @@ -1,16 +1,15 @@ import { Module } from '@nestjs/common'; -import { expect } from 'chai'; import { LazyModuleLoader, ModuleRef, ModulesContainer, NestContainer, -} from '../../../injector'; -import { Injector } from '../../../injector/injector'; -import { InstanceLoader } from '../../../injector/instance-loader'; -import { GraphInspector } from '../../../inspector/graph-inspector'; -import { MetadataScanner } from '../../../metadata-scanner'; -import { DependenciesScanner } from '../../../scanner'; +} from '../../../injector/index.js'; +import { Injector } from '../../../injector/injector.js'; +import { InstanceLoader } from '../../../injector/instance-loader.js'; +import { GraphInspector } from '../../../inspector/graph-inspector.js'; +import { MetadataScanner } from '../../../metadata-scanner.js'; +import { DependenciesScanner } from '../../../scanner.js'; describe('LazyModuleLoader', () => { let lazyModuleLoader: LazyModuleLoader; @@ -60,8 +59,8 @@ describe('LazyModuleLoader', () => { describe('when module was not loaded yet', () => { it('should load it and return a module reference', async () => { const moduleRef = await lazyModuleLoader.load(() => ModuleA); - expect(moduleRef).to.be.instanceOf(ModuleRef); - expect(moduleRef.get(bProvider.provide, { strict: false })).to.equal( + expect(moduleRef).toBeInstanceOf(ModuleRef); + expect(moduleRef.get(bProvider.provide, { strict: false })).toBe( bProvider.useValue, ); }); @@ -73,7 +72,7 @@ describe('LazyModuleLoader', () => { it('should return an existing module reference', async () => { const moduleRef = await lazyModuleLoader.load(() => ModuleC); const moduleRef2 = await lazyModuleLoader.load(() => ModuleC); - expect(moduleRef).to.equal(moduleRef2); + expect(moduleRef).toBe(moduleRef2); }); }); }); diff --git a/packages/core/test/injector/module.spec.ts b/packages/core/test/injector/module.spec.ts index b2ef4090ce9..095d3beeaf4 100644 --- a/packages/core/test/injector/module.spec.ts +++ b/packages/core/test/injector/module.spec.ts @@ -1,14 +1,13 @@ import { Controller, Scope } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Injectable } from '../../../common'; -import { Module as ModuleDecorator } from '../../../common/decorators/modules/module.decorator'; -import { RuntimeException } from '../../errors/exceptions/runtime.exception'; -import { UnknownElementException } from '../../errors/exceptions/unknown-element.exception'; -import { UnknownExportException } from '../../errors/exceptions/unknown-export.exception'; -import { NestContainer } from '../../injector/container'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { Module } from '../../injector/module'; +import { Module as ModuleDecorator } from '../../../common/decorators/modules/module.decorator.js'; +import { Injectable } from '../../../common/index.js'; +import { RuntimeException } from '../../errors/exceptions/runtime.exception.js'; +import { UnknownElementException } from '../../errors/exceptions/unknown-element.exception.js'; +import { UnknownExportException } from '../../errors/exceptions/unknown-export.exception.js'; +import { STATIC_CONTEXT } from '../../injector/constants.js'; +import { NestContainer } from '../../injector/container.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { Module } from '../../injector/module.js'; describe('Module', () => { let moduleRef: Module; @@ -29,206 +28,188 @@ describe('Module', () => { it('should add controller', () => { const collection = new Map(); - const setSpy = sinon.spy(collection, 'set'); + const setSpy = vi.spyOn(collection, 'set'); untypedModuleRef._controllers = collection; @Controller({ scope: Scope.REQUEST, durable: true }) class Test {} moduleRef.addController(Test); + expect(setSpy).toHaveBeenCalledOnce(); + const [key, wrapper] = setSpy.mock.calls[0]; + expect(key).toBe(Test); + expect(wrapper).toBeInstanceOf(InstanceWrapper); + expect(wrapper.token).toBe(Test); + expect(wrapper.name).toBe('Test'); + expect(wrapper.scope).toBe(Scope.REQUEST); + expect(wrapper.metatype).toBe(Test); + expect(wrapper.durable).toBe(true); + expect(wrapper.instance).toBeNull(); expect( - setSpy.calledWith( - Test, - new InstanceWrapper({ - host: moduleRef, - token: Test, - name: 'Test', - scope: Scope.REQUEST, - metatype: Test, - durable: true, - instance: null, - isResolved: false, - }), - ), - ).to.be.true; + wrapper.getInstanceByContextId(STATIC_CONTEXT).isResolved, + ).toBeFalsy(); }); it('should add injectable', () => { const collection = new Map(); - const setSpy = sinon.spy(collection, 'set'); + const setSpy = vi.spyOn(collection, 'set'); untypedModuleRef._injectables = collection; moduleRef.addInjectable(TestProvider, 'interceptor', TestModule); + expect(setSpy).toHaveBeenCalledOnce(); + const [key, wrapper] = setSpy.mock.calls[0]; + expect(key).toBe(TestProvider); + expect(wrapper).toBeInstanceOf(InstanceWrapper); + expect(wrapper.name).toBe('TestProvider'); + expect(wrapper.token).toBe(TestProvider); + expect(wrapper.metatype).toBe(TestProvider); + expect(wrapper.instance).toBeNull(); expect( - setSpy.calledWith( - TestProvider, - new InstanceWrapper({ - host: moduleRef, - name: 'TestProvider', - token: TestProvider, - scope: undefined, - metatype: TestProvider, - instance: null, - durable: undefined, - isResolved: false, - subtype: 'interceptor', - }), - ), - ).to.be.true; + wrapper.getInstanceByContextId(STATIC_CONTEXT).isResolved, + ).toBeFalsy(); + expect(wrapper.subtype).toBe('interceptor'); }); describe('when injectable is custom provided', () => { it('should call `addCustomProvider`', () => { - const addCustomProviderSpy = sinon.spy(moduleRef, 'addCustomProvider'); + const addCustomProviderSpy = vi.spyOn(moduleRef, 'addCustomProvider'); moduleRef.addInjectable({ provide: 'test' } as any, 'guard'); - expect(addCustomProviderSpy.called).to.be.true; + expect(addCustomProviderSpy).toHaveBeenCalled(); }); }); it('should add provider', () => { const collection = new Map(); - const setSpy = sinon.spy(collection, 'set'); + const setSpy = vi.spyOn(collection, 'set'); untypedModuleRef._providers = collection; moduleRef.addProvider(TestProvider); + expect(setSpy).toHaveBeenCalledOnce(); + const [key, wrapper] = setSpy.mock.calls[0]; + expect(key).toBe(TestProvider); + expect(wrapper).toBeInstanceOf(InstanceWrapper); + expect(wrapper.name).toBe('TestProvider'); + expect(wrapper.token).toBe(TestProvider); + expect(wrapper.metatype).toBe(TestProvider); + expect(wrapper.instance).toBeNull(); expect( - setSpy.calledWith( - TestProvider, - new InstanceWrapper({ - host: moduleRef, - name: 'TestProvider', - token: TestProvider, - scope: undefined, - metatype: TestProvider, - durable: undefined, - instance: null, - isResolved: false, - }), - ), - ).to.be.true; + wrapper.getInstanceByContextId(STATIC_CONTEXT).isResolved, + ).toBeFalsy(); }); it('should call "addCustomProvider" when "provide" property exists', () => { - const addCustomProvider = sinon.spy(); + const addCustomProvider = vi.fn(); moduleRef.addCustomProvider = addCustomProvider; const provider = { provide: 'test', useValue: 'test' }; moduleRef.addProvider(provider as any); - expect(addCustomProvider.called).to.be.true; + expect(addCustomProvider).toHaveBeenCalled(); }); it('should call "addCustomClass" when "useClass" property exists', () => { - const addCustomClass = sinon.spy(); + const addCustomClass = vi.fn(); moduleRef.addCustomClass = addCustomClass; const provider = { provide: 'test', useClass: () => null }; moduleRef.addCustomProvider(provider as any, new Map()); - expect(addCustomClass.called).to.be.true; + expect(addCustomClass).toHaveBeenCalled(); }); it('should call "addCustomValue" when "useValue" property exists', () => { - const addCustomValue = sinon.spy(); + const addCustomValue = vi.fn(); moduleRef.addCustomValue = addCustomValue; const provider = { provide: 'test', useValue: () => null }; moduleRef.addCustomProvider(provider as any, new Map()); - expect(addCustomValue.called).to.be.true; + expect(addCustomValue).toHaveBeenCalled(); }); it('should call "addCustomValue" when "useValue" property exists but its value is `undefined`', () => { - const addCustomValue = sinon.spy(); + const addCustomValue = vi.fn(); moduleRef.addCustomValue = addCustomValue; const provider = { provide: 'test', useValue: undefined }; moduleRef.addCustomProvider(provider as any, new Map()); - expect(addCustomValue.called).to.be.true; + expect(addCustomValue).toHaveBeenCalled(); }); it('should call "addCustomFactory" when "useFactory" property exists', () => { - const addCustomFactory = sinon.spy(); + const addCustomFactory = vi.fn(); moduleRef.addCustomFactory = addCustomFactory; const provider = { provide: 'test', useFactory: () => null }; moduleRef.addCustomProvider(provider as any, new Map()); - expect(addCustomFactory.called).to.be.true; + expect(addCustomFactory).toHaveBeenCalled(); }); it('should call "addCustomUseExisting" when "useExisting" property exists', () => { - const addCustomUseExisting = sinon.spy(); + const addCustomUseExisting = vi.fn(); moduleRef.addCustomUseExisting = addCustomUseExisting; const provider = { provide: 'test', useExisting: () => null }; moduleRef.addCustomUseExisting(provider as any, new Map()); - expect(addCustomUseExisting.called).to.be.true; + expect(addCustomUseExisting).toHaveBeenCalled(); }); describe('addCustomClass', () => { const type = { name: 'TypeTest' }; const provider = { provide: type, useClass: type, durable: true }; - let setSpy: sinon.SinonSpy; + let setSpy: ReturnType; beforeEach(() => { const collection = new Map(); - setSpy = sinon.spy(collection, 'set'); + setSpy = vi.spyOn(collection, 'set'); untypedModuleRef._providers = collection; }); it('should store provider', () => { moduleRef.addCustomClass(provider as any, untypedModuleRef._providers); + expect(setSpy).toHaveBeenCalledOnce(); + const [key, wrapper] = setSpy.mock.calls[0]; + expect(key).toBe(provider.provide); + expect(wrapper).toBeInstanceOf(InstanceWrapper); + expect(wrapper.token).toBe(type); + expect(wrapper.name).toBe(provider.provide.name); + expect(wrapper.metatype).toBe(type); + expect(wrapper.durable).toBe(true); + expect(wrapper.instance).toBeNull(); expect( - setSpy.calledWith( - provider.provide, - new InstanceWrapper({ - host: moduleRef, - token: type as any, - name: provider.provide.name, - scope: undefined, - metatype: type as any, - durable: true, - instance: null, - isResolved: false, - subtype: undefined, - }), - ), - ).to.be.true; + wrapper.getInstanceByContextId(STATIC_CONTEXT).isResolved, + ).toBeFalsy(); }); }); describe('addCustomValue', () => { - let setSpy: sinon.SinonSpy; + let setSpy: ReturnType; const value = () => ({}); const provider = { provide: value, useValue: value }; beforeEach(() => { const collection = new Map(); - setSpy = sinon.spy(collection, 'set'); + setSpy = vi.spyOn(collection, 'set'); untypedModuleRef._providers = collection; }); it('should store provider', () => { moduleRef.addCustomValue(provider as any, untypedModuleRef._providers); + expect(setSpy).toHaveBeenCalledOnce(); + const [key, wrapper] = setSpy.mock.calls[0]; + expect(key).toBe(provider.provide); + expect(wrapper).toBeInstanceOf(InstanceWrapper); + expect(wrapper.token).toBe(provider.provide); + expect(wrapper.name).toBe(provider.provide.name); + expect(wrapper.scope).toBe(Scope.DEFAULT); + expect(wrapper.metatype).toBeNull(); + expect(wrapper.instance).toBe(value); expect( - setSpy.calledWith( - provider.provide, - new InstanceWrapper({ - host: moduleRef, - token: provider.provide, - name: provider.provide.name, - scope: Scope.DEFAULT, - metatype: null, - instance: value, - isResolved: true, - async: false, - subtype: undefined, - }), - ), - ).to.be.true; + wrapper.getInstanceByContextId(STATIC_CONTEXT).isResolved, + ).toBeTruthy(); }); }); @@ -237,32 +218,28 @@ describe('Module', () => { const inject = [1, 2, 3]; const provider = { provide: type, useFactory: type, inject, durable: true }; - let setSpy: sinon.SinonSpy; + let setSpy: ReturnType; beforeEach(() => { const collection = new Map(); - setSpy = sinon.spy(collection, 'set'); + setSpy = vi.spyOn(collection, 'set'); untypedModuleRef._providers = collection; }); it('should store provider', () => { moduleRef.addCustomFactory(provider as any, untypedModuleRef._providers); + expect(setSpy).toHaveBeenCalledOnce(); + const [key, wrapper] = setSpy.mock.calls[0]; + expect(key).toBe(provider.provide); + expect(wrapper).toBeInstanceOf(InstanceWrapper); + expect(wrapper.token).toBe(provider.provide); + expect(wrapper.name).toBe(provider.provide.name); + expect(wrapper.metatype).toBe(type); + expect(wrapper.durable).toBe(true); + expect(wrapper.instance).toBeNull(); expect( - setSpy.calledWith( - provider.provide, - new InstanceWrapper({ - host: moduleRef, - token: provider.provide as any, - name: provider.provide.name, - scope: undefined, - metatype: type as any, - durable: true, - instance: null, - isResolved: false, - inject: inject as any, - subtype: undefined, - }), - ), - ).to.be.true; + wrapper.getInstanceByContextId(STATIC_CONTEXT).isResolved, + ).toBeFalsy(); + expect(wrapper.inject).toEqual(inject); }); }); @@ -270,10 +247,10 @@ describe('Module', () => { const type = { name: 'TypeTest' }; const provider = { provide: type, useExisting: type }; - let setSpy: sinon.SinonSpy; + let setSpy: ReturnType; beforeEach(() => { const collection = new Map(); - setSpy = sinon.spy(collection, 'set'); + setSpy = vi.spyOn(collection, 'set'); untypedModuleRef._providers = collection; }); it('should store provider', () => { @@ -286,64 +263,63 @@ describe('Module', () => { ).metatype; const token = provider.provide as any; + expect(setSpy).toHaveBeenCalledOnce(); + const [key, wrapper] = setSpy.mock.calls[0]; + expect(key).toBe(token); + expect(wrapper).toBeInstanceOf(InstanceWrapper); + expect(wrapper.token).toBe(token); + expect(wrapper.name).toBe(provider.provide.name); + expect(wrapper.metatype).toBe(factoryFn); + expect(wrapper.instance).toBeNull(); + expect(wrapper.inject).toEqual([provider.useExisting]); expect( - setSpy.calledWith( - token, - new InstanceWrapper({ - host: moduleRef, - token, - name: provider.provide.name, - metatype: factoryFn, - instance: null, - inject: [provider.useExisting as any], - isResolved: false, - isAlias: true, - subtype: undefined, - }), - ), - ).to.be.true; - expect(factoryFn(provider.useExisting)).to.be.eql(type); + wrapper.getInstanceByContextId(STATIC_CONTEXT).isResolved, + ).toBeFalsy(); + expect(wrapper.isAlias).toBe(true); + expect(factoryFn(provider.useExisting)).toEqual(type); }); }); describe('when get instance', () => { describe('when metatype does not exists in providers collection', () => { beforeEach(() => { - sinon.stub(untypedModuleRef._providers, 'has').returns(false); + vi.spyOn(untypedModuleRef._providers, 'has').mockReturnValue(false); }); it('should throw RuntimeException', () => { - expect(() => moduleRef.instance).to.throws(RuntimeException); + expect(() => moduleRef.instance).toThrow(RuntimeException); }); }); describe('when metatype exists in providers collection', () => { it('should return null', () => { - expect(moduleRef.instance).to.be.eql(null); + expect(moduleRef.instance).toEqual(null); }); }); }); describe('when exported provider is custom provided', () => { beforeEach(() => { - sinon.stub(moduleRef, 'validateExportedProvider').callsFake(o => o); + vi.spyOn(moduleRef, 'validateExportedProvider').mockImplementation( + o => o, + ); }); it('should call `addCustomExportedProvider`', () => { - const addCustomExportedProviderSpy = sinon.spy( + const addCustomExportedProviderSpy = vi.spyOn( moduleRef, 'addCustomExportedProvider', ); moduleRef.addExportedProviderOrModule({ provide: 'test' } as any); - expect(addCustomExportedProviderSpy.called).to.be.true; + expect(addCustomExportedProviderSpy).toHaveBeenCalled(); }); it('should support symbols', () => { - const addCustomExportedProviderSpy = sinon.spy( + const addCustomExportedProviderSpy = vi.spyOn( moduleRef, 'addCustomExportedProvider', ); const symb = Symbol('test'); moduleRef.addExportedProviderOrModule({ provide: symb } as any); - expect(addCustomExportedProviderSpy.called).to.be.true; - expect(untypedModuleRef._exports.has(symb)).to.be.true; + expect(addCustomExportedProviderSpy).toHaveBeenCalled(); + expect(untypedModuleRef._exports.has(symb)).toBe(true); }); }); @@ -351,28 +327,30 @@ describe('Module', () => { describe('when provider', () => { it('should call `mergeWith`', () => { const wrapper = { - mergeWith: sinon.spy(), + mergeWith: vi.fn(), }; - sinon.stub(moduleRef, 'hasProvider').callsFake(() => true); - sinon.stub(moduleRef.providers, 'get').callsFake(() => wrapper as any); + vi.spyOn(moduleRef, 'hasProvider').mockImplementation(() => true); + vi.spyOn(moduleRef.providers, 'get').mockImplementation( + () => wrapper as any, + ); moduleRef.replace(null!, { isProvider: true }); - expect(wrapper.mergeWith.called).to.be.true; + expect(wrapper.mergeWith).toHaveBeenCalled(); }); }); describe('when guard', () => { it('should call `mergeWith`', () => { const wrapper = { - mergeWith: sinon.spy(), + mergeWith: vi.fn(), isProvider: true, }; - sinon.stub(moduleRef, 'hasInjectable').callsFake(() => true); - sinon - .stub(moduleRef.injectables, 'get') - .callsFake(() => wrapper as any); + vi.spyOn(moduleRef, 'hasInjectable').mockImplementation(() => true); + vi.spyOn(moduleRef.injectables, 'get').mockImplementation( + () => wrapper as any, + ); moduleRef.replace(null!, {}); - expect(wrapper.mergeWith.called).to.be.true; + expect(wrapper.mergeWith).toHaveBeenCalled(); }); }); }); @@ -382,7 +360,7 @@ describe('Module', () => { const test = ['test']; untypedModuleRef._imports = test; - expect(moduleRef.imports).to.be.eql(test); + expect(moduleRef.imports).toEqual(test); }); }); @@ -390,7 +368,7 @@ describe('Module', () => { it('should return injectables', () => { const test = ['test']; untypedModuleRef._injectables = test; - expect(moduleRef.injectables).to.be.eql(test); + expect(moduleRef.injectables).toEqual(test); }); }); @@ -399,7 +377,7 @@ describe('Module', () => { const test = ['test']; untypedModuleRef._controllers = test; - expect(moduleRef.controllers).to.be.eql(test); + expect(moduleRef.controllers).toEqual(test); }); }); @@ -408,7 +386,7 @@ describe('Module', () => { const test = ['test']; untypedModuleRef._exports = test; - expect(moduleRef.exports).to.be.eql(test); + expect(moduleRef.exports).toEqual(test); }); }); @@ -417,7 +395,7 @@ describe('Module', () => { const test = ['test']; untypedModuleRef._providers = test; - expect(moduleRef.providers).to.be.eql(test); + expect(moduleRef.providers).toEqual(test); }); }); @@ -430,11 +408,11 @@ describe('Module', () => { }); it('should return metatype with "get" method', () => { - expect(!!customModuleRef.get).to.be.true; + expect(!!customModuleRef.get).toBe(true); }); describe('get', () => { it('should throw exception if not exists', () => { - expect(() => customModuleRef.get('fail')).to.throws( + expect(() => customModuleRef.get('fail')).toThrow( UnknownElementException, ); }); @@ -446,7 +424,7 @@ describe('Module', () => { describe('when unit exists in provider collection', () => { it('should behave as identity', () => { untypedModuleRef._providers = new Map([[token, true]]); - expect(moduleRef.validateExportedProvider(token)).to.be.eql(token); + expect(moduleRef.validateExportedProvider(token)).toEqual(token); }); }); describe('when unit exists in related modules collection', () => { @@ -455,12 +433,12 @@ describe('Module', () => { untypedModuleRef._imports = new Set([ new Module(Random, new NestContainer()), ]); - expect(moduleRef.validateExportedProvider(Random)).to.be.eql(Random); + expect(moduleRef.validateExportedProvider(Random)).toEqual(Random); }); }); describe('when unit does not exist in both provider and related modules collections', () => { it('should throw UnknownExportException', () => { - expect(() => moduleRef.validateExportedProvider(token)).to.throws( + expect(() => moduleRef.validateExportedProvider(token)).toThrow( UnknownExportException, ); }); @@ -472,12 +450,12 @@ describe('Module', () => { it('should return true', () => { const token = 'test'; moduleRef.providers.set(token, new InstanceWrapper()); - expect(moduleRef.hasProvider(token)).to.be.true; + expect(moduleRef.hasProvider(token)).toBe(true); }); }); describe('otherwise', () => { it('should return false', () => { - expect(moduleRef.hasProvider('_')).to.be.false; + expect(moduleRef.hasProvider('_')).toBe(false); }); }); }); @@ -487,32 +465,32 @@ describe('Module', () => { it('should return true', () => { const token = 'test'; moduleRef.injectables.set(token, new InstanceWrapper()); - expect(moduleRef.hasInjectable(token)).to.be.true; + expect(moduleRef.hasInjectable(token)).toBe(true); }); }); describe('otherwise', () => { it('should return false', () => { - expect(moduleRef.hasInjectable('_')).to.be.false; + expect(moduleRef.hasInjectable('_')).toBe(false); }); }); }); describe('getter "id"', () => { it('should return module id', () => { - expect(moduleRef.id).to.be.equal(moduleRef['_id']); + expect(moduleRef.id).toBe(moduleRef['_id']); }); }); describe('getProviderByKey', () => { describe('when does not exist', () => { it('should return undefined', () => { - expect(moduleRef.getProviderByKey('test')).to.be.undefined; + expect(moduleRef.getProviderByKey('test')).toBeUndefined(); }); }); describe('otherwise', () => { it('should return instance wrapper', () => { moduleRef.addProvider(TestProvider); - expect(moduleRef.getProviderByKey(TestProvider)).to.not.be.undefined; + expect(moduleRef.getProviderByKey(TestProvider)).not.toBeUndefined(); }); }); }); diff --git a/packages/core/test/injector/modules-container.spec.ts b/packages/core/test/injector/modules-container.spec.ts new file mode 100644 index 00000000000..46e7b3b2b2e --- /dev/null +++ b/packages/core/test/injector/modules-container.spec.ts @@ -0,0 +1,58 @@ +import { firstValueFrom } from 'rxjs'; +import { ModulesContainer } from '../../injector/modules-container.js'; + +describe('ModulesContainer', () => { + let container: ModulesContainer; + + beforeEach(() => { + container = new ModulesContainer(); + }); + + describe('applicationId', () => { + it('should return a string identifier', () => { + expect(typeof container.applicationId).toBe('string'); + }); + + it('should return the same id on subsequent calls', () => { + expect(container.applicationId).toBe(container.applicationId); + }); + + it('should return different ids for different containers', () => { + const container2 = new ModulesContainer(); + expect(container.applicationId).not.toBe(container2.applicationId); + }); + }); + + describe('getById', () => { + it('should return a module by its id', () => { + const mockModule = { id: 'test-id' } as any; + container.set('key', mockModule); + + expect(container.getById('test-id')).toBe(mockModule); + }); + + it('should return undefined if no module matches', () => { + expect(container.getById('non-existent')).toBeUndefined(); + }); + }); + + describe('getRpcTargetRegistry / addRpcTarget', () => { + it('should emit targets added via addRpcTarget', async () => { + const target = { pattern: 'test' }; + const promise = firstValueFrom(container.getRpcTargetRegistry()); + + container.addRpcTarget(target); + + const result = await promise; + expect(result).toBe(target); + }); + + it('should replay previous targets to new subscribers', async () => { + container.addRpcTarget('first'); + container.addRpcTarget('second'); + + const result = await firstValueFrom(container.getRpcTargetRegistry()); + expect(result).toBe('first'); + }); + }); +}); diff --git a/packages/core/test/injector/nested-transient-isolation.spec.ts b/packages/core/test/injector/nested-transient-isolation.spec.ts index 6078f72eb5f..4e6cbefe7a6 100644 --- a/packages/core/test/injector/nested-transient-isolation.spec.ts +++ b/packages/core/test/injector/nested-transient-isolation.spec.ts @@ -1,10 +1,9 @@ import { Scope } from '@nestjs/common'; -import { expect } from 'chai'; -import { Injectable } from '../../../common/decorators/core/injectable.decorator'; -import { NestContainer } from '../../injector/container'; -import { Injector } from '../../injector/injector'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { Module } from '../../injector/module'; +import { Injectable } from '../../../common/decorators/core/injectable.decorator.js'; +import { NestContainer } from '../../injector/container.js'; +import { Injector } from '../../injector/injector.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { Module } from '../../injector/module.js'; describe('Nested Transient Isolation', () => { let injector: Injector; @@ -117,7 +116,7 @@ describe('Nested Transient Isolation', () => { parent2Wrapper.getInstanceByContextId(contextId).instance; // 각 parent는 서로 다른 TransientService instance를 가져야 함 - expect(parent1Instance.transient.instanceId).to.not.equal( + expect(parent1Instance.transient.instanceId).not.toBe( parent2Instance.transient.instanceId, ); }); @@ -144,7 +143,7 @@ describe('Nested Transient Isolation', () => { parent2Wrapper.getInstanceByContextId(contextId).instance; // 각 TransientService는 서로 다른 NestedTransientService instance를 가져야 함 - expect(parent1Instance.transient.nested.instanceId).to.not.equal( + expect(parent1Instance.transient.nested.instanceId).not.toBe( parent2Instance.transient.nested.instanceId, ); }); @@ -180,12 +179,12 @@ describe('Nested Transient Isolation', () => { parent1Wrapper.getInstanceByContextId(contextId2).instance; // 같은 context 내에서 다른 parent - expect(ctx1Parent1.transient.nested.instanceId).to.not.equal( + expect(ctx1Parent1.transient.nested.instanceId).not.toBe( ctx1Parent2.transient.nested.instanceId, ); // 다른 context의 같은 parent - expect(ctx1Parent1.transient.nested.instanceId).to.not.equal( + expect(ctx1Parent1.transient.nested.instanceId).not.toBe( ctx2Parent1.transient.nested.instanceId, ); }); @@ -258,15 +257,13 @@ describe('Nested Transient Isolation', () => { const parentInstance = parentWrapper.instance; - expect(TransientService.constructorCalled).to.be.true; - expect(NestedTransientService.constructorCalled).to.be.true; - expect(parentInstance.transient).to.be.instanceOf(TransientService); - expect(parentInstance.transient.nested).to.be.instanceOf( + expect(TransientService.constructorCalled).toBe(true); + expect(NestedTransientService.constructorCalled).toBe(true); + expect(parentInstance.transient).toBeInstanceOf(TransientService); + expect(parentInstance.transient.nested).toBeInstanceOf( NestedTransientService, ); - expect(parentInstance.transient.nested.value).to.equal( - 'nested-initialized', - ); + expect(parentInstance.transient.nested.value).toBe('nested-initialized'); }); }); }); diff --git a/packages/core/test/injector/opaque-key-factory/by-reference-module-opaque-key-factory.spec.ts b/packages/core/test/injector/opaque-key-factory/by-reference-module-opaque-key-factory.spec.ts index 2d9db42eb57..79d85db47b2 100644 --- a/packages/core/test/injector/opaque-key-factory/by-reference-module-opaque-key-factory.spec.ts +++ b/packages/core/test/injector/opaque-key-factory/by-reference-module-opaque-key-factory.spec.ts @@ -1,6 +1,4 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ByReferenceModuleOpaqueKeyFactory } from '../../../injector/opaque-key-factory/by-reference-module-opaque-key-factory'; +import { ByReferenceModuleOpaqueKeyFactory } from '../../../injector/opaque-key-factory/by-reference-module-opaque-key-factory.js'; describe('ByReferenceModuleOpaqueKeyFactory', () => { const moduleId = 'constId'; @@ -9,7 +7,9 @@ describe('ByReferenceModuleOpaqueKeyFactory', () => { describe('when generating algorithm is random', () => { beforeEach(() => { factory = new ByReferenceModuleOpaqueKeyFactory(); - sinon.stub(factory as any, 'generateRandomString').returns(moduleId); + vi.spyOn(factory as any, 'generateRandomString').mockReturnValue( + moduleId, + ); }); describe('createForStatic', () => { @@ -19,7 +19,7 @@ describe('ByReferenceModuleOpaqueKeyFactory', () => { const type = Module; const token1 = factory.createForStatic(type); const token2 = factory.createForStatic(type); - expect(token1).to.be.deep.eq(token2); + expect(token1).toEqual(token2); }); }); @@ -51,7 +51,7 @@ describe('ByReferenceModuleOpaqueKeyFactory', () => { dynamicModule, ); - expect(token1).to.be.deep.eq(token2); + expect(token1).toEqual(token2); }); }); }); @@ -60,7 +60,9 @@ describe('ByReferenceModuleOpaqueKeyFactory', () => { factory = new ByReferenceModuleOpaqueKeyFactory({ keyGenerationStrategy: 'shallow', }); - sinon.stub(factory as any, 'generateRandomString').returns(moduleId); + vi.spyOn(factory as any, 'generateRandomString').mockReturnValue( + moduleId, + ); }); describe('createForStatic', () => { @@ -71,7 +73,7 @@ describe('ByReferenceModuleOpaqueKeyFactory', () => { const token1 = factory.createForStatic(type); const token2 = factory.createForStatic(type); - expect(token1).to.be.deep.eq(token2); + expect(token1).toEqual(token2); }); }); @@ -104,7 +106,7 @@ describe('ByReferenceModuleOpaqueKeyFactory', () => { dynamicModule, ); - expect(token1).to.be.deep.eq(token2); + expect(token1).toEqual(token2); }); }); }); diff --git a/packages/core/test/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.spec.ts b/packages/core/test/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.spec.ts index 28d05a6cb14..97867f4d61b 100644 --- a/packages/core/test/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.spec.ts +++ b/packages/core/test/injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.spec.ts @@ -1,6 +1,4 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { DeepHashedModuleOpaqueKeyFactory } from '../../../injector/opaque-key-factory/deep-hashed-module-opaque-key-factory'; +import { DeepHashedModuleOpaqueKeyFactory } from '../../../injector/opaque-key-factory/deep-hashed-module-opaque-key-factory.js'; describe('DeepHashedModuleOpaqueKeyFactory', () => { const moduleId = 'constId'; @@ -8,7 +6,7 @@ describe('DeepHashedModuleOpaqueKeyFactory', () => { beforeEach(() => { factory = new DeepHashedModuleOpaqueKeyFactory(); - sinon.stub(factory, 'getModuleId').returns(moduleId); + vi.spyOn(factory, 'getModuleId').mockReturnValue(moduleId); }); describe('createForStatic', () => { class Module {} @@ -17,7 +15,7 @@ describe('DeepHashedModuleOpaqueKeyFactory', () => { const type = Module; const token1 = factory.createForStatic(type); const token2 = factory.createForStatic(type); - expect(token1).to.be.deep.eq(token2); + expect(token1).toEqual(token2); }); }); describe('createForDynamic', () => { @@ -32,14 +30,14 @@ describe('DeepHashedModuleOpaqueKeyFactory', () => { providers: [{}], } as any); - expect(token1).to.be.deep.eq(token2); + expect(token1).toEqual(token2); }); }); describe('getModuleName', () => { it('should map module metatype to name', () => { const metatype = () => {}; - expect(factory.getModuleName(metatype as any)).to.be.eql(metatype.name); + expect(factory.getModuleName(metatype as any)).toEqual(metatype.name); }); }); @@ -47,14 +45,14 @@ describe('DeepHashedModuleOpaqueKeyFactory', () => { describe('when metadata exists', () => { it('should return hash', () => { const metadata = { providers: ['', {}] }; - expect(factory.getStringifiedOpaqueToken(metadata as any)).to.be.eql( + expect(factory.getStringifiedOpaqueToken(metadata as any)).toEqual( JSON.stringify(metadata), ); }); it('should return hash with class', () => { class Provider {} const metadata = { providers: [Provider], exports: [Provider] }; - expect(factory.getStringifiedOpaqueToken(metadata)).to.be.eql( + expect(factory.getStringifiedOpaqueToken(metadata)).toEqual( '{"providers":["Provider"],"exports":["Provider"]}', ); }); @@ -64,7 +62,7 @@ describe('DeepHashedModuleOpaqueKeyFactory', () => { useValue: function Provider() {}, }; const metadata = { providers: [provider] }; - expect(factory.getStringifiedOpaqueToken(metadata)).to.be.eql( + expect(factory.getStringifiedOpaqueToken(metadata)).toEqual( `{"providers":[{"provide":"ProvideValue","useValue":"${provider.useValue.toString()}"}]}`, ); }); @@ -82,7 +80,7 @@ describe('DeepHashedModuleOpaqueKeyFactory', () => { ], }; - expect(factory.getStringifiedOpaqueToken(metadata)).to.be.eql( + expect(factory.getStringifiedOpaqueToken(metadata)).toEqual( '{"providers":[{"provide":"Symbol(a)","useValue":"a"},{"provide":"Symbol(b)","useValue":"b"}]}', ); }); @@ -90,7 +88,7 @@ describe('DeepHashedModuleOpaqueKeyFactory', () => { describe('when metadata does not exist', () => { it('should return empty string', () => { - expect(factory.getStringifiedOpaqueToken(undefined)).to.be.eql(''); + expect(factory.getStringifiedOpaqueToken(undefined)).toEqual(''); }); }); }); diff --git a/packages/core/test/injector/topology-tree/topology-tree.spec.ts b/packages/core/test/injector/topology-tree/topology-tree.spec.ts new file mode 100644 index 00000000000..84e5930d4c7 --- /dev/null +++ b/packages/core/test/injector/topology-tree/topology-tree.spec.ts @@ -0,0 +1,148 @@ +import { TopologyTree } from '../../../injector/topology-tree/topology-tree.js'; + +// Minimal mock of Module to exercise the tree construction logic. +function createModuleMock( + name: string, + imports: any[] = [], +): Record { + return { name, imports, id: name }; +} + +describe('TopologyTree', () => { + describe('basic construction', () => { + it('should create a tree from a single module with no imports', () => { + const root = createModuleMock('Root'); + const tree = new TopologyTree(root as any); + + const visited: Array<{ value: any; depth: number }> = []; + tree.walk((value, depth) => visited.push({ value, depth })); + + expect(visited).toHaveLength(1); + expect(visited[0].value).toBe(root); + expect(visited[0].depth).toBe(1); + }); + + it('should create a tree with child modules', () => { + const childA = createModuleMock('A'); + const childB = createModuleMock('B'); + const root = createModuleMock('Root', [childA, childB]); + + const tree = new TopologyTree(root as any); + + const visited: string[] = []; + tree.walk((value: any) => visited.push(value.name)); + + expect(visited).toContain('Root'); + expect(visited).toContain('A'); + expect(visited).toContain('B'); + expect(visited).toHaveLength(3); + }); + }); + + describe('walk', () => { + it('should walk with correct depth values', () => { + const grandchild = createModuleMock('Grandchild'); + const child = createModuleMock('Child', [grandchild]); + const root = createModuleMock('Root', [child]); + + const tree = new TopologyTree(root as any); + + const depths: Record = {}; + tree.walk((value: any, depth) => { + depths[value.name] = depth; + }); + + expect(depths['Root']).toBe(1); + expect(depths['Child']).toBe(2); + expect(depths['Grandchild']).toBe(3); + }); + }); + + describe('cyclic imports', () => { + it('should handle modules that import each other', () => { + const a = createModuleMock('A'); + const b = createModuleMock('B'); + // Create a cycle: A imports B, B imports A + a.imports = [b]; + b.imports = [a]; + const root = createModuleMock('Root', [a]); + + // Should not throw or infinite-loop + const tree = new TopologyTree(root as any); + + const visited: string[] = []; + tree.walk((value: any) => visited.push(value.name)); + + expect(visited).toContain('Root'); + expect(visited).toContain('A'); + expect(visited).toContain('B'); + }); + }); + + describe('null/undefined imports', () => { + it('should skip null entries in imports', () => { + const child = createModuleMock('Child'); + const root = createModuleMock('Root', [null, child, undefined]); + + const tree = new TopologyTree(root as any); + + const visited: string[] = []; + tree.walk((value: any) => visited.push(value.name)); + + expect(visited).toContain('Root'); + expect(visited).toContain('Child'); + expect(visited).toHaveLength(2); + }); + }); + + describe('shared imports (diamond dependency)', () => { + it('should handle the same module imported by multiple parents', () => { + const shared = createModuleMock('Shared'); + const childA = createModuleMock('A', [shared]); + const childB = createModuleMock('B', [shared]); + const root = createModuleMock('Root', [childA, childB]); + + const tree = new TopologyTree(root as any); + + const visited: string[] = []; + tree.walk((value: any) => visited.push(value.name)); + + // Shared should appear exactly once (it is relinked, not duplicated) + expect(visited.filter(n => n === 'Shared')).toHaveLength(1); + }); + + it('should relink a shared module to the deeper parent', () => { + const shared = createModuleMock('Shared'); + // childA is at depth 2, childB also at depth 2 + // shared first seen via childA (depth 2), then via childB (depth 2) + const childA = createModuleMock('A', [shared]); + const childB = createModuleMock('B', [shared]); + const root = createModuleMock('Root', [childA, childB]); + + const tree = new TopologyTree(root as any); + + const depths: Record = {}; + tree.walk((value: any, depth) => { + depths[value.name] = depth; + }); + + // Shared should be at depth 3 (child of either A or B, both at depth 2) + expect(depths['Shared']).toBe(3); + }); + }); + + describe('modules with no imports property', () => { + it('should handle modules without an imports property', () => { + const leaf = { name: 'Leaf', id: 'Leaf' } as any; // no imports + const root = createModuleMock('Root', [leaf]); + + const tree = new TopologyTree(root as any); + + const visited: string[] = []; + tree.walk((value: any) => visited.push(value.name)); + + expect(visited).toContain('Root'); + expect(visited).toContain('Leaf'); + }); + }); +}); diff --git a/packages/core/test/injector/topology-tree/tree-node.spec.ts b/packages/core/test/injector/topology-tree/tree-node.spec.ts index 267ba44d93f..6d8b12c1cf6 100644 --- a/packages/core/test/injector/topology-tree/tree-node.spec.ts +++ b/packages/core/test/injector/topology-tree/tree-node.spec.ts @@ -1,22 +1,21 @@ -import { expect } from 'chai'; -import { TreeNode } from '../../../injector/topology-tree/tree-node'; +import { TreeNode } from '../../../injector/topology-tree/tree-node.js'; describe('TreeNode', () => { describe('constructor', () => { it('should create a node with the given value', () => { const node = new TreeNode({ value: 'test', parent: null }); - expect(node.value).to.equal('test'); + expect(node.value).toBe('test'); }); it('should create a node with null parent', () => { const node = new TreeNode({ value: 'test', parent: null }); - expect(node.children.size).to.equal(0); + expect(node.children.size).toBe(0); }); it('should create a node with a parent', () => { const parent = new TreeNode({ value: 'parent', parent: null }); const child = new TreeNode({ value: 'child', parent }); - expect(child.value).to.equal('child'); + expect(child.value).toBe('child'); }); }); @@ -27,8 +26,8 @@ describe('TreeNode', () => { parent.addChild(child); - expect(parent.children.has(child)).to.be.true; - expect(parent.children.size).to.equal(1); + expect(parent.children.has(child)).toBe(true); + expect(parent.children.size).toBe(1); }); it('should add multiple children', () => { @@ -39,7 +38,7 @@ describe('TreeNode', () => { parent.addChild(child1); parent.addChild(child2); - expect(parent.children.size).to.equal(2); + expect(parent.children.size).toBe(2); }); }); @@ -51,8 +50,8 @@ describe('TreeNode', () => { parent.addChild(child); parent.removeChild(child); - expect(parent.children.has(child)).to.be.false; - expect(parent.children.size).to.equal(0); + expect(parent.children.has(child)).toBe(false); + expect(parent.children.size).toBe(0); }); it('should do nothing when removing a non-existent child', () => { @@ -61,7 +60,7 @@ describe('TreeNode', () => { parent.removeChild(child); - expect(parent.children.size).to.equal(0); + expect(parent.children.size).toBe(0); }); }); @@ -74,8 +73,8 @@ describe('TreeNode', () => { oldParent.addChild(child); child.relink(newParent); - expect(oldParent.children.has(child)).to.be.false; - expect(newParent.children.has(child)).to.be.true; + expect(oldParent.children.has(child)).toBe(false); + expect(newParent.children.has(child)).toBe(true); }); it('should work when node has no previous parent', () => { @@ -84,20 +83,20 @@ describe('TreeNode', () => { child.relink(newParent); - expect(newParent.children.has(child)).to.be.true; + expect(newParent.children.has(child)).toBe(true); }); }); describe('getDepth', () => { it('should return 1 for a root node', () => { const root = new TreeNode({ value: 'root', parent: null }); - expect(root.getDepth()).to.equal(1); + expect(root.getDepth()).toBe(1); }); it('should return 2 for a child of root', () => { const root = new TreeNode({ value: 'root', parent: null }); const child = new TreeNode({ value: 'child', parent: root }); - expect(child.getDepth()).to.equal(2); + expect(child.getDepth()).toBe(2); }); it('should return correct depth for deeply nested nodes', () => { @@ -106,7 +105,7 @@ describe('TreeNode', () => { const level2 = new TreeNode({ value: 'level2', parent: level1 }); const level3 = new TreeNode({ value: 'level3', parent: level2 }); - expect(level3.getDepth()).to.equal(4); + expect(level3.getDepth()).toBe(4); }); it('should return -1 when a cycle is detected', () => { @@ -117,7 +116,7 @@ describe('TreeNode', () => { // Create cycle: A -> B -> C -> A nodeA.relink(nodeC); - expect(nodeA.getDepth()).to.equal(-1); + expect(nodeA.getDepth()).toBe(-1); }); }); @@ -126,24 +125,24 @@ describe('TreeNode', () => { const root = new TreeNode({ value: 'root', parent: null }); const child = new TreeNode({ value: 'child', parent: root }); - expect(child.hasCycleWith('nonexistent')).to.be.false; + expect(child.hasCycleWith('nonexistent')).toBe(false); }); it('should return true when the target value exists in the parent chain', () => { const root = new TreeNode({ value: 'root', parent: null }); const child = new TreeNode({ value: 'child', parent: root }); - expect(child.hasCycleWith('root')).to.be.true; + expect(child.hasCycleWith('root')).toBe(true); }); it('should return true when checking against own value', () => { const node = new TreeNode({ value: 'self', parent: null }); - expect(node.hasCycleWith('self')).to.be.true; + expect(node.hasCycleWith('self')).toBe(true); }); it('should return false for root node with non-matching value', () => { const root = new TreeNode({ value: 'root', parent: null }); - expect(root.hasCycleWith('other')).to.be.false; + expect(root.hasCycleWith('other')).toBe(false); }); it('should return false when cycle exists but target not in chain', () => { @@ -154,7 +153,7 @@ describe('TreeNode', () => { // Create cycle: A -> B -> C -> A nodeA.relink(nodeC); - expect(nodeA.hasCycleWith('nonexistent')).to.be.false; + expect(nodeA.hasCycleWith('nonexistent')).toBe(false); }); }); }); diff --git a/packages/core/test/inspector/deterministic-uuid-registry.spec.ts b/packages/core/test/inspector/deterministic-uuid-registry.spec.ts new file mode 100644 index 00000000000..64036289cb5 --- /dev/null +++ b/packages/core/test/inspector/deterministic-uuid-registry.spec.ts @@ -0,0 +1,44 @@ +import { DeterministicUuidRegistry } from '../../inspector/deterministic-uuid-registry.js'; + +describe('DeterministicUuidRegistry', () => { + afterEach(() => { + DeterministicUuidRegistry.clear(); + }); + + describe('get', () => { + it('should return a deterministic string id for a given input', () => { + const id = DeterministicUuidRegistry.get('test'); + expect(typeof id).toBe('string'); + }); + + it('should return the same id for the same input after clearing', () => { + const id1 = DeterministicUuidRegistry.get('hello'); + DeterministicUuidRegistry.clear(); + const id2 = DeterministicUuidRegistry.get('hello'); + expect(id1).toBe(id2); + }); + + it('should return different ids for different inputs', () => { + const id1 = DeterministicUuidRegistry.get('input-a'); + const id2 = DeterministicUuidRegistry.get('input-b'); + expect(id1).not.toBe(id2); + }); + + it('should handle collisions by incrementing', () => { + // Getting the same string twice should produce different IDs (collision avoidance) + const id1 = DeterministicUuidRegistry.get('same-string'); + const id2 = DeterministicUuidRegistry.get('same-string'); + expect(id1).not.toBe(id2); + }); + }); + + describe('clear', () => { + it('should reset the registry', () => { + const id1 = DeterministicUuidRegistry.get('test-clear'); + DeterministicUuidRegistry.clear(); + // After clearing, the same string should produce the same ID (no collision) + const id2 = DeterministicUuidRegistry.get('test-clear'); + expect(id1).toBe(id2); + }); + }); +}); diff --git a/packages/core/test/inspector/graph-inspector.spec.ts b/packages/core/test/inspector/graph-inspector.spec.ts index d24780341c0..8be406b443f 100644 --- a/packages/core/test/inspector/graph-inspector.spec.ts +++ b/packages/core/test/inspector/graph-inspector.spec.ts @@ -1,12 +1,10 @@ import { Scope } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { NestContainer } from '../../injector/container'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { Module } from '../../injector/module'; -import { GraphInspector } from '../../inspector/graph-inspector'; -import { EnhancerMetadataCacheEntry } from '../../inspector/interfaces/enhancer-metadata-cache-entry.interface'; -import { SerializedGraph } from '../../inspector/serialized-graph'; +import { NestContainer } from '../../injector/container.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { Module } from '../../injector/module.js'; +import { GraphInspector } from '../../inspector/graph-inspector.js'; +import { EnhancerMetadataCacheEntry } from '../../inspector/interfaces/enhancer-metadata-cache-entry.interface.js'; +import { SerializedGraph } from '../../inspector/serialized-graph.js'; describe('GraphInspector', () => { let graphInspector: GraphInspector; @@ -70,7 +68,7 @@ describe('GraphInspector', () => { graphInspector.inspectInstanceWrapper(instanceWrapper, moduleRef); const edgesArr = [...graph['edges'].values()]; - expect(edgesArr).to.deep.equal([ + expect(edgesArr).toEqual([ { id: edgesArr[0].id, metadata: { @@ -164,11 +162,13 @@ describe('GraphInspector', () => { ); const serializedNode = { metadata: {} }; - sinon.stub(graph, 'getNodeById').callsFake(() => serializedNode as any); + vi.spyOn(graph, 'getNodeById').mockImplementation( + () => serializedNode as any, + ); graphInspector.inspectModules(); - expect(serializedNode).to.deep.equal({ + expect(serializedNode).toEqual({ metadata: { enhancers: [ { methodKey, name: RandomPipe.name, subtype }, @@ -207,11 +207,11 @@ describe('GraphInspector', () => { graphInspector.insertAttachedEnhancer(instanceWrapper); - expect(insertedNode.metadata).to.deep.equal({ + expect(insertedNode.metadata).toEqual({ ...nodeDefinition.metadata, global: true, }); - expect(graph['extras'].attachedEnhancers).to.deep.contain({ + expect(graph['extras'].attachedEnhancers).toContainEqual({ nodeId: insertedNode.id, }); }); diff --git a/packages/core/test/inspector/noop-graph-inspector.spec.ts b/packages/core/test/inspector/noop-graph-inspector.spec.ts new file mode 100644 index 00000000000..536111c51a2 --- /dev/null +++ b/packages/core/test/inspector/noop-graph-inspector.spec.ts @@ -0,0 +1,33 @@ +import { NoopGraphInspector } from '../../inspector/noop-graph-inspector.js'; + +describe('NoopGraphInspector', () => { + it('should be defined', () => { + expect(NoopGraphInspector).toBeDefined(); + }); + + it('should return a noop function for any property access', () => { + const anyMethod = (NoopGraphInspector as any).insertNode; + expect(typeof anyMethod).toBe('function'); + }); + + it('should not throw when calling any method', () => { + expect(() => (NoopGraphInspector as any).insertNode({})).not.toThrow(); + expect(() => (NoopGraphInspector as any).insertEdge({})).not.toThrow(); + expect(() => + (NoopGraphInspector as any).insertEntrypoint({}, 'id'), + ).not.toThrow(); + }); + + it('should return undefined from noop calls', () => { + const result = (NoopGraphInspector as any).insertNode({}); + expect(result).toBeUndefined(); + }); + + it('should return the same noop function for different properties', () => { + const fn1 = (NoopGraphInspector as any).insertNode; + const fn2 = (NoopGraphInspector as any).insertEdge; + // Both should be the same noop function since the proxy returns the same one + expect(typeof fn1).toBe('function'); + expect(typeof fn2).toBe('function'); + }); +}); diff --git a/packages/core/test/inspector/partial-graph.host.spec.ts b/packages/core/test/inspector/partial-graph.host.spec.ts new file mode 100644 index 00000000000..26d9235385d --- /dev/null +++ b/packages/core/test/inspector/partial-graph.host.spec.ts @@ -0,0 +1,50 @@ +import { PartialGraphHost } from '../../inspector/partial-graph.host.js'; +import { SerializedGraph } from '../../inspector/serialized-graph.js'; + +describe('PartialGraphHost', () => { + afterEach(() => { + // Reset the private static + (PartialGraphHost as any).partialGraph = undefined; + }); + + describe('register', () => { + it('should store the graph for later retrieval', () => { + const graph = new SerializedGraph(); + PartialGraphHost.register(graph); + + expect(PartialGraphHost.toJSON()).toBeDefined(); + }); + }); + + describe('toJSON', () => { + it('should return undefined when no graph is registered', () => { + expect(PartialGraphHost.toJSON()).toBeUndefined(); + }); + + it('should delegate to the registered graph', () => { + const graph = new SerializedGraph(); + const spy = vi.spyOn(graph, 'toJSON'); + PartialGraphHost.register(graph); + + PartialGraphHost.toJSON(); + + expect(spy).toHaveBeenCalledOnce(); + }); + }); + + describe('toString', () => { + it('should return undefined when no graph is registered', () => { + expect(PartialGraphHost.toString()).toBeUndefined(); + }); + + it('should delegate to the registered graph', () => { + const graph = new SerializedGraph(); + const spy = vi.spyOn(graph, 'toString'); + PartialGraphHost.register(graph); + + PartialGraphHost.toString(); + + expect(spy).toHaveBeenCalledOnce(); + }); + }); +}); diff --git a/packages/core/test/inspector/serialized-graph.spec.ts b/packages/core/test/inspector/serialized-graph.spec.ts index 25c5bd15a61..357574bb899 100644 --- a/packages/core/test/inspector/serialized-graph.spec.ts +++ b/packages/core/test/inspector/serialized-graph.spec.ts @@ -1,9 +1,8 @@ import { Scope } from '@nestjs/common'; -import { expect } from 'chai'; -import { ApplicationConfig } from '../../application-config'; -import { Edge } from '../../inspector/interfaces/edge.interface'; -import { Node } from '../../inspector/interfaces/node.interface'; -import { SerializedGraph } from '../../inspector/serialized-graph'; +import { ApplicationConfig } from '../../application-config.js'; +import { Edge } from '../../inspector/interfaces/edge.interface.js'; +import { Node } from '../../inspector/interfaces/node.interface.js'; +import { SerializedGraph } from '../../inspector/serialized-graph.js'; describe('SerializedGraph', () => { let serializedGraph: SerializedGraph; @@ -37,7 +36,7 @@ describe('SerializedGraph', () => { }; serializedGraph.insertNode(nodeDefinition); - expect(nodesCollection.get(nodeDefinition.id)).to.deep.equal({ + expect(nodesCollection.get(nodeDefinition.id)).toEqual({ ...nodeDefinition, metadata: { ...nodeDefinition.metadata, @@ -68,7 +67,7 @@ describe('SerializedGraph', () => { }; serializedGraph.insertNode(nodeDefinition); - expect(nodesCollection.get(nodeDefinition.id)).to.equal(nodeDefinition); + expect(nodesCollection.get(nodeDefinition.id)).toBe(nodeDefinition); }); }); }); @@ -94,7 +93,7 @@ describe('SerializedGraph', () => { }; const edge = serializedGraph.insertEdge(edgeDefinition); - expect(edgesCollection.get(edge.id)).to.deep.equal({ + expect(edgesCollection.get(edge.id)).toEqual({ ...edgeDefinition, metadata: { ...edgeDefinition.metadata, @@ -124,7 +123,7 @@ describe('SerializedGraph', () => { }; const edge = serializedGraph.insertEdge(edgeDefinition); - expect(edgesCollection.get(edge.id)).to.deep.equal({ + expect(edgesCollection.get(edge.id)).toEqual({ ...edgeDefinition, id: edge.id, }); @@ -152,9 +151,182 @@ describe('SerializedGraph', () => { }; nodesCollection.set(nodeDefinition.id, nodeDefinition); - expect(serializedGraph.getNodeById(nodeDefinition.id)).to.eq( + expect(serializedGraph.getNodeById(nodeDefinition.id)).toBe( nodeDefinition, ); }); + + it('should return undefined for non-existent id', () => { + expect(serializedGraph.getNodeById('non-existent')).toBeUndefined(); + }); + }); + + describe('insertNode (duplicate)', () => { + it('should return the existing node when the same id is inserted twice', () => { + class AppService {} + + const nodeDefinition = { + id: 'duplicate-id', + label: 'AppService', + parent: 'parent-id', + metadata: { + type: 'provider' as const, + sourceModuleName: 'AppModule', + durable: false, + static: true, + transient: false, + token: AppService, + scope: Scope.DEFAULT, + exported: false, + initTime: 100, + }, + }; + + const first = serializedGraph.insertNode(nodeDefinition); + const second = serializedGraph.insertNode({ + ...nodeDefinition, + label: 'DifferentLabel', + }); + + // Should return the original, not the new one + expect(second).toBe(first); + expect(nodesCollection.get('duplicate-id')!.label).toBe('AppService'); + }); + }); + + describe('insertEntrypoint', () => { + it('should create a new entrypoint collection for a parent id', () => { + const entrypoint = { + type: 'http-endpoint', + methodName: 'findAll', + className: 'UsersController', + classNodeId: 'node-1', + }; + serializedGraph.insertEntrypoint(entrypoint as any, 'parent-1'); + + const json = serializedGraph.toJSON(); + expect(json.entrypoints['parent-1']).toHaveLength(1); + expect(json.entrypoints['parent-1'][0]).toBe(entrypoint); + }); + + it('should append to existing collection for the same parent id', () => { + const ep1 = { type: 'http', methodName: 'findAll' }; + const ep2 = { type: 'http', methodName: 'findOne' }; + + serializedGraph.insertEntrypoint(ep1 as any, 'parent-1'); + serializedGraph.insertEntrypoint(ep2 as any, 'parent-1'); + + const json = serializedGraph.toJSON(); + expect(json.entrypoints['parent-1']).toHaveLength(2); + }); + }); + + describe('insertOrphanedEnhancer', () => { + it('should add an orphaned enhancer entry', () => { + const entry = { subtype: 'guard', ref: 'AuthGuard' }; + serializedGraph.insertOrphanedEnhancer(entry as any); + + const json = serializedGraph.toJSON(); + expect(json.extras.orphanedEnhancers).toContainEqual(entry); + }); + }); + + describe('insertAttachedEnhancer', () => { + it('should add an attached enhancer with the given nodeId', () => { + serializedGraph.insertAttachedEnhancer('node-123'); + + const json = serializedGraph.toJSON(); + expect(json.extras.attachedEnhancers).toContainEqual({ + nodeId: 'node-123', + }); + }); + }); + + describe('status and metadata setters', () => { + it('should include status in JSON output', () => { + serializedGraph.status = 'partial'; + + const json = serializedGraph.toJSON(); + expect(json['status']).toBe('partial'); + }); + + it('should default status to complete', () => { + const json = serializedGraph.toJSON(); + expect(json['status']).toBe('complete'); + }); + + it('should include metadata in JSON output', () => { + const metadata = { key: 'value' }; + serializedGraph.metadata = metadata as any; + + const json = serializedGraph.toJSON(); + expect(json['metadata']).toBe(metadata); + }); + }); + + describe('toJSON', () => { + it('should return a JSON representation with all collections', () => { + const json = serializedGraph.toJSON(); + + expect(json).toHaveProperty('nodes'); + expect(json).toHaveProperty('edges'); + expect(json).toHaveProperty('entrypoints'); + expect(json).toHaveProperty('extras'); + }); + }); + + describe('toString', () => { + it('should return a JSON string representation', () => { + const result = serializedGraph.toString(); + expect(() => JSON.parse(result)).not.toThrow(); + }); + + it('should convert symbol values to strings', () => { + const sym = Symbol('testSymbol'); + const nodeDefinition = { + id: 'sym-node', + label: 'SymNode', + parent: 'parent', + metadata: { + type: 'provider' as const, + sourceModuleName: 'AppModule', + durable: false, + static: true, + transient: false, + token: sym as any, + scope: Scope.DEFAULT, + exported: false, + initTime: 0, + }, + }; + serializedGraph.insertNode(nodeDefinition); + + const result = serializedGraph.toString(); + expect(result).toContain('Symbol(testSymbol)'); + }); + + it('should convert function values to their names', () => { + function MyService() {} + const nodeDefinition = { + id: 'fn-node', + label: 'FnNode', + parent: 'parent', + metadata: { + type: 'provider' as const, + sourceModuleName: 'AppModule', + durable: false, + static: true, + transient: false, + token: MyService, + scope: Scope.DEFAULT, + exported: false, + initTime: 0, + }, + }; + serializedGraph.insertNode(nodeDefinition); + + const result = serializedGraph.toString(); + expect(result).toContain('MyService'); + }); }); }); diff --git a/packages/core/test/inspector/uuid-factory.spec.ts b/packages/core/test/inspector/uuid-factory.spec.ts new file mode 100644 index 00000000000..90764441ec9 --- /dev/null +++ b/packages/core/test/inspector/uuid-factory.spec.ts @@ -0,0 +1,49 @@ +import { DeterministicUuidRegistry } from '../../inspector/deterministic-uuid-registry.js'; +import { UuidFactory, UuidFactoryMode } from '../../inspector/uuid-factory.js'; + +describe('UuidFactory', () => { + afterEach(() => { + UuidFactory.mode = UuidFactoryMode.Random; + DeterministicUuidRegistry.clear(); + }); + + describe('when mode is Random', () => { + it('should return a random string', () => { + UuidFactory.mode = UuidFactoryMode.Random; + const id = UuidFactory.get('key'); + expect(typeof id).toBe('string'); + expect(id.length).toBeGreaterThan(0); + }); + + it('should return different values on subsequent calls', () => { + UuidFactory.mode = UuidFactoryMode.Random; + const id1 = UuidFactory.get('key'); + const id2 = UuidFactory.get('key'); + expect(id1).not.toBe(id2); + }); + }); + + describe('when mode is Deterministic', () => { + it('should return a deterministic id for the same key', () => { + UuidFactory.mode = UuidFactoryMode.Deterministic; + const id1 = UuidFactory.get('same-key'); + DeterministicUuidRegistry.clear(); + const id2 = UuidFactory.get('same-key'); + expect(id1).toBe(id2); + }); + + it('should return different ids for different keys', () => { + UuidFactory.mode = UuidFactoryMode.Deterministic; + const id1 = UuidFactory.get('key-a'); + const id2 = UuidFactory.get('key-b'); + expect(id1).not.toBe(id2); + }); + }); + + describe('get without key', () => { + it('should work when called without arguments', () => { + const id = UuidFactory.get(); + expect(typeof id).toBe('string'); + }); + }); +}); diff --git a/packages/core/test/interceptors/interceptors-consumer.spec.ts b/packages/core/test/interceptors/interceptors-consumer.spec.ts index e560be19f7c..afe7ee07c4a 100644 --- a/packages/core/test/interceptors/interceptors-consumer.spec.ts +++ b/packages/core/test/interceptors/interceptors-consumer.spec.ts @@ -1,9 +1,7 @@ import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common'; import { AsyncLocalStorage } from 'async_hooks'; -import { expect } from 'chai'; import { Observable, defer, lastValueFrom, merge, of, retry } from 'rxjs'; -import * as sinon from 'sinon'; -import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer'; +import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer.js'; describe('InterceptorsConsumer', () => { let consumer: InterceptorsConsumer; @@ -12,30 +10,32 @@ describe('InterceptorsConsumer', () => { consumer = new InterceptorsConsumer(); interceptors = [ { - intercept: sinon.stub().callsFake((ctx, handler) => handler.handle()), + intercept: vi + .fn() + .mockImplementation((ctx, handler) => handler.handle()), }, { - intercept: sinon - .stub() - .callsFake(async (ctx, handler) => handler.handle()), + intercept: vi + .fn() + .mockImplementation(async (ctx, handler) => handler.handle()), }, ]; }); describe('intercept', () => { describe('when interceptors array is empty', () => { - let next: sinon.SinonSpy; + let next: ReturnType; beforeEach(() => { - next = sinon.spy(); + next = vi.fn(); }); it('should call next()', async () => { await consumer.intercept([], null!, { constructor: null }, null!, next); - expect(next.calledOnce).to.be.true; + expect(next).toHaveBeenCalledOnce(); }); }); describe('when interceptors array is not empty', () => { - let next: sinon.SinonSpy; + let next: ReturnType; beforeEach(() => { - next = sinon.stub().returns(Promise.resolve('')); + next = vi.fn().mockReturnValue(Promise.resolve('')); }); it('does not call `intercept` (lazy evaluation)', async () => { await consumer.intercept( @@ -46,8 +46,8 @@ describe('InterceptorsConsumer', () => { next, ); - expect(interceptors[0].intercept.called).to.be.false; - expect(interceptors[1].intercept.called).to.be.false; + expect(interceptors[0].intercept).not.toHaveBeenCalled(); + expect(interceptors[1].intercept).not.toHaveBeenCalled(); }); it('should call every `intercept` method when subscribe', async () => { const intercepted = await consumer.intercept( @@ -59,8 +59,8 @@ describe('InterceptorsConsumer', () => { ); await transformToResult(intercepted); - expect(interceptors[0].intercept.calledOnce).to.be.true; - expect(interceptors[1].intercept.calledOnce).to.be.true; + expect(interceptors[0].intercept).toHaveBeenCalledOnce(); + expect(interceptors[1].intercept).toHaveBeenCalledOnce(); }); it('should not call `next` (lazy evaluation)', async () => { await consumer.intercept( @@ -70,7 +70,7 @@ describe('InterceptorsConsumer', () => { null!, next, ); - expect(next.called).to.be.false; + expect(next).not.toHaveBeenCalled(); }); it('should call `next` when subscribe', async () => { const intercepted = await consumer.intercept( @@ -81,7 +81,7 @@ describe('InterceptorsConsumer', () => { next, ); await transformToResult(intercepted); - expect(next.called).to.be.true; + expect(next).toHaveBeenCalled(); }); }); @@ -107,7 +107,7 @@ describe('InterceptorsConsumer', () => { next, ); const result = await transformToResult(intercepted); - expect(result).to.equal('hello'); + expect(result).toBe('hello'); }); }); @@ -136,7 +136,7 @@ describe('InterceptorsConsumer', () => { null!, next, ); - expect(await transformToResult(intercepted)).to.equal(3); + expect(await transformToResult(intercepted)).toBe(3); }); }); }); @@ -146,8 +146,8 @@ describe('InterceptorsConsumer', () => { const callback = () => null; const context = consumer.createContext([], instance, callback); - expect(context.getClass()).to.be.eql(instance.constructor); - expect(context.getHandler()).to.be.eql(callback); + expect(context.getClass()).toEqual(instance.constructor); + expect(context.getHandler()).toEqual(callback); }); }); describe('transformDeferred', () => { @@ -155,7 +155,7 @@ describe('InterceptorsConsumer', () => { it('should return Observable', async () => { const val = 3; const next = async () => val; - expect(await lastValueFrom(consumer.transformDeferred(next))).to.be.eql( + expect(await lastValueFrom(consumer.transformDeferred(next))).toEqual( val, ); }); @@ -164,7 +164,7 @@ describe('InterceptorsConsumer', () => { it('should return Observable', async () => { const val = 3; const next = async () => val; - expect(await lastValueFrom(consumer.transformDeferred(next))).to.be.eql( + expect(await lastValueFrom(consumer.transformDeferred(next))).toEqual( val, ); }); @@ -175,7 +175,7 @@ describe('InterceptorsConsumer', () => { const next = async () => of(val); expect( await lastValueFrom(consumer.transformDeferred(next) as any), - ).to.be.eql(val); + ).toEqual(val); }); }); }); @@ -184,7 +184,7 @@ describe('InterceptorsConsumer', () => { class TestError extends Error {} const testInterceptors = [ { - intercept: sinon.stub().callsFake(async (ctx, handler) => { + intercept: vi.fn().mockImplementation(async (ctx, handler) => { return merge( handler.handle(), defer(() => { @@ -194,14 +194,14 @@ describe('InterceptorsConsumer', () => { }), }, { - intercept: sinon - .stub() - .callsFake(async (ctx, handler) => handler.handle()), + intercept: vi + .fn() + .mockImplementation(async (ctx, handler) => handler.handle()), }, { - intercept: sinon - .stub() - .callsFake(async (ctx, handler) => handler.handle()), + intercept: vi + .fn() + .mockImplementation(async (ctx, handler) => handler.handle()), }, ]; @@ -220,7 +220,7 @@ describe('InterceptorsConsumer', () => { throw error; } } - expect(testInterceptors[2].intercept.called).to.be.false; + expect(testInterceptors[2].intercept).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/interceptors/interceptors-context-creator.spec.ts b/packages/core/test/interceptors/interceptors-context-creator.spec.ts index 8e9bd5ce9d1..a2bb9fb195b 100644 --- a/packages/core/test/interceptors/interceptors-context-creator.spec.ts +++ b/packages/core/test/interceptors/interceptors-context-creator.spec.ts @@ -1,9 +1,7 @@ -import { expect } from 'chai'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { ApplicationConfig } from '../../application-config'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { InterceptorsContextCreator } from '../../interceptors/interceptors-context-creator'; +import { ApplicationConfig } from '../../application-config.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { InterceptorsContextCreator } from '../../interceptors/interceptors-context-creator.js'; class Interceptor {} @@ -12,7 +10,7 @@ describe('InterceptorsContextCreator', () => { let interceptors: any[]; let applicationConfig: ApplicationConfig; let container: any; - let getSpy: sinon.SinonSpy; + let getSpy: ReturnType; class Interceptor1 {} class Interceptor2 {} @@ -40,7 +38,7 @@ describe('InterceptorsContextCreator', () => { {}, undefined, ]; - getSpy = sinon.stub().returns({ + getSpy = vi.fn().mockReturnValue({ injectables: new Map([ [Interceptor1, interceptors[0]], [Interceptor2, interceptors[1]], @@ -62,7 +60,7 @@ describe('InterceptorsContextCreator', () => { it('should return empty array', () => { const result = interceptorsContextCreator.createConcreteContext(interceptors); - expect(result).to.be.empty; + expect(result).toHaveLength(0); }); }); describe('when `moduleContext` is defined', () => { @@ -76,7 +74,7 @@ describe('InterceptorsContextCreator', () => { ]; expect( interceptorsContextCreator.createConcreteContext(interceptorTypeRefs), - ).to.have.length(2); + ).toHaveLength(2); }); }); }); @@ -87,7 +85,7 @@ describe('InterceptorsContextCreator', () => { const instance = { intercept: () => null! }; expect( interceptorsContextCreator.getInterceptorInstance(instance), - ).to.be.eql(instance); + ).toEqual(instance); }); }); describe('when param is a constructor', () => { @@ -96,20 +94,22 @@ describe('InterceptorsContextCreator', () => { instance: 'test', getInstanceByContextId: () => wrapper, } as any; - sinon - .stub(interceptorsContextCreator, 'getInstanceByMetatype') - .callsFake(() => wrapper); + vi.spyOn( + interceptorsContextCreator, + 'getInstanceByMetatype', + ).mockImplementation(() => wrapper); expect( interceptorsContextCreator.getInterceptorInstance(Interceptor), - ).to.be.eql(wrapper.instance); + ).toEqual(wrapper.instance); }); it('should return null', () => { - sinon - .stub(interceptorsContextCreator, 'getInstanceByMetatype') - .callsFake(() => null!); + vi.spyOn( + interceptorsContextCreator, + 'getInstanceByMetatype', + ).mockImplementation(() => null!); expect( interceptorsContextCreator.getInterceptorInstance(Interceptor), - ).to.be.eql(null); + ).toEqual(null); }); }); }); @@ -118,8 +118,9 @@ describe('InterceptorsContextCreator', () => { describe('when "moduleContext" is nil', () => { it('should return undefined', () => { (interceptorsContextCreator as any).moduleContext = undefined; - expect(interceptorsContextCreator.getInstanceByMetatype(null!)).to.be - .undefined; + expect( + interceptorsContextCreator.getInstanceByMetatype(null!), + ).toBeUndefined(); }); }); describe('when "moduleContext" is not nil', () => { @@ -129,8 +130,9 @@ describe('InterceptorsContextCreator', () => { describe('and when module exists', () => { it('should return undefined', () => { - expect(interceptorsContextCreator.getInstanceByMetatype(class {})).to - .be.undefined; + expect( + interceptorsContextCreator.getInstanceByMetatype(class {}), + ).toBeUndefined(); }); }); }); @@ -140,7 +142,7 @@ describe('InterceptorsContextCreator', () => { describe('when contextId is static and inquirerId is nil', () => { it('should return global interceptors', () => { const expectedResult = applicationConfig.getGlobalInterceptors(); - expect(interceptorsContextCreator.getGlobalMetadata()).to.be.equal( + expect(interceptorsContextCreator.getGlobalMetadata()).toBe( expectedResult, ); }); @@ -152,19 +154,20 @@ describe('InterceptorsContextCreator', () => { const instance = 'request-scoped'; const scopedInterceptorWrappers = [instanceWrapper]; - sinon - .stub(applicationConfig, 'getGlobalInterceptors') - .callsFake(() => globalInterceptors); - sinon - .stub(applicationConfig, 'getGlobalRequestInterceptors') - .callsFake(() => scopedInterceptorWrappers); - sinon - .stub(instanceWrapper, 'getInstanceByContextId') - .callsFake(() => ({ instance }) as any); + vi.spyOn(applicationConfig, 'getGlobalInterceptors').mockImplementation( + () => globalInterceptors, + ); + vi.spyOn( + applicationConfig, + 'getGlobalRequestInterceptors', + ).mockImplementation(() => scopedInterceptorWrappers); + vi.spyOn(instanceWrapper, 'getInstanceByContextId').mockImplementation( + () => ({ instance }) as any, + ); - expect( - interceptorsContextCreator.getGlobalMetadata({ id: 3 }), - ).to.contains(instance, ...globalInterceptors); + expect(interceptorsContextCreator.getGlobalMetadata({ id: 3 })).toEqual( + expect.arrayContaining([instance, ...globalInterceptors]), + ); }); }); }); diff --git a/packages/core/test/metadata-scanner.spec.ts b/packages/core/test/metadata-scanner.spec.ts index dd2c78b3f2f..7ad35963a02 100644 --- a/packages/core/test/metadata-scanner.spec.ts +++ b/packages/core/test/metadata-scanner.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { MetadataScanner } from '../metadata-scanner'; +import { MetadataScanner } from '../metadata-scanner.js'; describe('MetadataScanner', () => { let scanner: MetadataScanner; @@ -31,13 +30,13 @@ describe('MetadataScanner', () => { it('should return only methods', () => { const methods = scanner.getAllMethodNames(Test.prototype); - expect(methods).to.eql(['test', 'test2', 'testParent', 'testParent2']); + expect(methods).toEqual(['test', 'test2', 'testParent', 'testParent2']); }); it('should return the same instance for the same prototype', () => { const methods1 = scanner.getAllMethodNames(Test.prototype); const methods2 = scanner.getAllMethodNames(Test.prototype); - expect(methods1 === methods2).to.eql(true); + expect(methods1 === methods2).toEqual(true); }); it('should keep compatibility with older methods', () => { @@ -48,14 +47,61 @@ describe('MetadataScanner', () => { r => r[0], ); - expect(methods1).to.eql(methods2); + expect(methods1).toEqual(methods2); const methods3 = scanner.getAllMethodNames(Test.prototype); const methods4 = [ ...new Set(scanner.getAllFilteredMethodNames(Test.prototype)), ]; - expect(methods3).to.eql(methods4); + expect(methods3).toEqual(methods4); + }); + }); + + describe('getAllMethodNames', () => { + it('should return empty array when prototype is null', () => { + expect(scanner.getAllMethodNames(null)).toEqual([]); + }); + + it('should exclude getters and setters', () => { + class WithAccessors { + get myProp() { + return 1; + } + set myProp(v) {} + method() {} + } + const methods = scanner.getAllMethodNames(WithAccessors.prototype); + expect(methods).toContain('method'); + expect(methods).not.toContain('myProp'); + }); + + it('should exclude non-function properties', () => { + function MyClass() {} + MyClass.prototype.stringProp = 'hello'; + MyClass.prototype.realMethod = function () {}; + const methods = scanner.getAllMethodNames(MyClass.prototype); + expect(methods).toContain('realMethod'); + expect(methods).not.toContain('stringProp'); + }); + }); + + describe('scanFromPrototype', () => { + it('should return empty array when prototype is null', () => { + expect(scanner.scanFromPrototype({}, null, () => 'x')).toEqual([]); + }); + + it('should skip nil return values from callback', () => { + class Simple { + a() {} + b() {} + } + const result = scanner.scanFromPrototype( + new Simple(), + Simple.prototype, + name => (name === 'a' ? name : undefined), + ); + expect(result).toEqual(['a']); }); }); }); diff --git a/packages/core/test/middleware/builder.spec.ts b/packages/core/test/middleware/builder.spec.ts index 0f625f71496..b95b05c72d3 100644 --- a/packages/core/test/middleware/builder.spec.ts +++ b/packages/core/test/middleware/builder.spec.ts @@ -1,4 +1,3 @@ -import { expect } from 'chai'; import { Controller, Delete, @@ -11,14 +10,14 @@ import { RequestMethod, Version, VersioningType, -} from '../../../common'; -import { MiddlewareConfigProxy } from '../../../common/interfaces'; -import { ApplicationConfig } from '../../application-config'; -import { NestContainer } from '../../injector/container'; -import { MiddlewareBuilder } from '../../middleware/builder'; -import { RouteInfoPathExtractor } from '../../middleware/route-info-path-extractor'; -import { RoutesMapper } from '../../middleware/routes-mapper'; -import { NoopHttpAdapter } from './../utils/noop-adapter.spec'; +} from '../../../common/index.js'; +import { MiddlewareConfigProxy } from '../../../common/interfaces/index.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { NestContainer } from '../../injector/container.js'; +import { MiddlewareBuilder } from '../../middleware/builder.js'; +import { RouteInfoPathExtractor } from '../../middleware/route-info-path-extractor.js'; +import { RoutesMapper } from '../../middleware/routes-mapper.js'; +import { NoopHttpAdapter } from './../utils/noop-adapter.js'; describe('MiddlewareBuilder', () => { let builder: MiddlewareBuilder; @@ -37,7 +36,7 @@ describe('MiddlewareBuilder', () => { it('should return configuration proxy', () => { const configProxy = builder.apply([]); const metatype = (MiddlewareBuilder as any).ConfigProxy; - expect(configProxy instanceof metatype).to.be.true; + expect(configProxy instanceof metatype).toBe(true); }); describe('configuration proxy', () => { @@ -60,7 +59,7 @@ describe('MiddlewareBuilder', () => { it('should store configuration passed as argument', () => { configProxy.forRoutes(route, Test); - expect(builder.build()).to.deep.equal([ + expect(builder.build()).toEqual([ { middleware: [], forRoutes: [ @@ -124,7 +123,7 @@ describe('MiddlewareBuilder', () => { it('should remove overlapping routes', () => { configProxy.forRoutes(UsersController); - expect(builder.build()).to.deep.equal([ + expect(builder.build()).toEqual([ { middleware: [], forRoutes: [ @@ -190,7 +189,7 @@ describe('MiddlewareBuilder', () => { const path = '/test'; const proxy: any = builder.apply().exclude(path); - expect(proxy.getExcludedRoutes()).to.be.eql([ + expect(proxy.getExcludedRoutes()).toEqual([ { path, method: -1, diff --git a/packages/core/test/middleware/container.spec.ts b/packages/core/test/middleware/container.spec.ts index 1cf312ae069..d29e1ba0b31 100644 --- a/packages/core/test/middleware/container.spec.ts +++ b/packages/core/test/middleware/container.spec.ts @@ -1,14 +1,13 @@ -import { expect } from 'chai'; -import { Injectable } from '../../../common'; -import { Controller } from '../../../common/decorators/core/controller.decorator'; -import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator'; -import { RequestMethod } from '../../../common/enums/request-method.enum'; -import { MiddlewareConfiguration } from '../../../common/interfaces/middleware/middleware-configuration.interface'; -import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface'; -import { NestContainer } from '../../injector'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { Module } from '../../injector/module'; -import { MiddlewareContainer } from '../../middleware/container'; +import { Injectable } from '../../../common/index.js'; +import { Controller } from '../../../common/decorators/core/controller.decorator.js'; +import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator.js'; +import { RequestMethod } from '../../../common/enums/request-method.enum.js'; +import { MiddlewareConfiguration } from '../../../common/interfaces/middleware/middleware-configuration.interface.js'; +import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface.js'; +import { NestContainer } from '../../injector/index.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { Module } from '../../injector/module.js'; +import { MiddlewareContainer } from '../../middleware/container.js'; describe('MiddlewareContainer', () => { class ExampleModule {} @@ -47,9 +46,7 @@ describe('MiddlewareContainer', () => { }, ]; container.insertConfig(config, 'Module'); - expect([...container.getConfigurations().get('Module')!]).to.deep.equal( - config, - ); + expect([...container.getConfigurations().get('Module')!]).toEqual(config); }); it('should store expected middleware for given module', () => { @@ -66,9 +63,9 @@ describe('MiddlewareContainer', () => { const collection = container.getMiddlewareCollection(key); const insertedMiddleware = collection.get(TestMiddleware); - expect(collection.size).to.eql(config.length); - expect(insertedMiddleware).to.be.instanceOf(InstanceWrapper); - expect(insertedMiddleware!.scope).to.be.undefined; - expect(insertedMiddleware!.metatype).to.be.eql(TestMiddleware); + expect(collection.size).toEqual(config.length); + expect(insertedMiddleware).toBeInstanceOf(InstanceWrapper); + expect(insertedMiddleware!.scope).toBeUndefined(); + expect(insertedMiddleware!.metatype).toEqual(TestMiddleware); }); }); diff --git a/packages/core/test/middleware/middleware-module.spec.ts b/packages/core/test/middleware/middleware-module.spec.ts index 0b0453a25c4..7ab452bdc78 100644 --- a/packages/core/test/middleware/middleware-module.spec.ts +++ b/packages/core/test/middleware/middleware-module.spec.ts @@ -1,27 +1,21 @@ import { Injectable } from '@nestjs/common'; -import { RouteInfoPathExtractor } from '@nestjs/core/middleware/route-info-path-extractor'; -import * as chai from 'chai'; -import { expect } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; -import * as sinon from 'sinon'; -import { Controller } from '../../../common/decorators/core/controller.decorator'; -import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator'; -import { RequestMethod } from '../../../common/enums/request-method.enum'; -import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface'; -import { ApplicationConfig } from '../../application-config'; -import { InvalidMiddlewareException } from '../../errors/exceptions/invalid-middleware.exception'; -import { RuntimeException } from '../../errors/exceptions/runtime.exception'; -import { NestContainer } from '../../injector/container'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { Module } from '../../injector/module'; -import { GraphInspector } from '../../inspector/graph-inspector'; -import { MiddlewareBuilder } from '../../middleware/builder'; -import { MiddlewareContainer } from '../../middleware/container'; -import { MiddlewareModule } from '../../middleware/middleware-module'; -import { RouterExceptionFilters } from '../../router/router-exception-filters'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; - -chai.use(chaiAsPromised); +import { RouteInfoPathExtractor } from '@nestjs/core/middleware/route-info-path-extractor.js'; +import { Controller } from '../../../common/decorators/core/controller.decorator.js'; +import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator.js'; +import { RequestMethod } from '../../../common/enums/request-method.enum.js'; +import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { InvalidMiddlewareException } from '../../errors/exceptions/invalid-middleware.exception.js'; +import { RuntimeException } from '../../errors/exceptions/runtime.exception.js'; +import { NestContainer } from '../../injector/container.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { Module } from '../../injector/module.js'; +import { GraphInspector } from '../../inspector/graph-inspector.js'; +import { MiddlewareBuilder } from '../../middleware/builder.js'; +import { MiddlewareContainer } from '../../middleware/container.js'; +import { MiddlewareModule } from '../../middleware/middleware-module.js'; +import { RouterExceptionFilters } from '../../router/router-exception-filters.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('MiddlewareModule', () => { let middlewareModule: MiddlewareModule; @@ -72,7 +66,7 @@ describe('MiddlewareModule', () => { .getModules() .set('Test', new Module(class {}, stubContainer)); - const configureSpy = sinon.spy(); + const configureSpy = vi.fn(); const mockModule = { instance: { configure: configureSpy, @@ -86,16 +80,14 @@ describe('MiddlewareModule', () => { 'Test', ); - expect(configureSpy.calledOnce).to.be.true; - expect( - configureSpy.calledWith( - new MiddlewareBuilder( - (middlewareModule as any).routesMapper, - undefined!, - new RouteInfoPathExtractor(new ApplicationConfig()), - ), + expect(configureSpy).toHaveBeenCalledOnce(); + expect(configureSpy).toHaveBeenCalledWith( + new MiddlewareBuilder( + (middlewareModule as any).routesMapper, + undefined!, + new RouteInfoPathExtractor(new ApplicationConfig()), ), - ).to.be.true; + ); }); }); @@ -110,18 +102,18 @@ describe('MiddlewareModule', () => { .getModules() .set('Test', new Module(TestModule, nestContainer)); }); - it('should throw "RuntimeException" exception when middleware is not stored in container', () => { + it('should throw "RuntimeException" exception when middleware is not stored in container', async () => { const route = { path: 'Test' }; const configuration = { middleware: [TestMiddleware], forRoutes: [BaseController], }; - const useSpy = sinon.spy(); + const useSpy = vi.fn(); const app = { use: useSpy }; middlewareModule['container'] = nestContainer; - expect( + await expect( middlewareModule.registerRouteMiddleware( new MiddlewareContainer(nestContainer), route as any, @@ -129,10 +121,10 @@ describe('MiddlewareModule', () => { 'Test', app, ), - ).to.eventually.be.rejectedWith(RuntimeException); + ).rejects.toThrow(RuntimeException); }); - it('should throw "InvalidMiddlewareException" exception when middleware does not have "use" method', () => { + it('should throw "InvalidMiddlewareException" exception when middleware does not have "use" method', async () => { @Injectable() class InvalidMiddleware {} @@ -142,7 +134,7 @@ describe('MiddlewareModule', () => { forRoutes: [BaseController], }; - const useSpy = sinon.spy(); + const useSpy = vi.fn(); const app = { use: useSpy }; const container = new MiddlewareContainer(nestContainer); @@ -155,7 +147,9 @@ describe('MiddlewareModule', () => { instance, } as any); - expect( + middlewareModule['container'] = nestContainer; + + await expect( middlewareModule.registerRouteMiddleware( container, route as any, @@ -163,7 +157,7 @@ describe('MiddlewareModule', () => { moduleKey, app, ), - ).to.be.rejectedWith(InvalidMiddlewareException); + ).rejects.toThrow(InvalidMiddlewareException); }); it('should mount middleware when is stored in container', async () => { @@ -173,9 +167,9 @@ describe('MiddlewareModule', () => { forRoutes: ['test', BasicController, BaseController], }; - const createMiddlewareFactoryStub = sinon - .stub() - .callsFake(() => () => null); + const createMiddlewareFactoryStub = vi + .fn() + .mockImplementation(() => () => null); const app = { createMiddlewareFactory: createMiddlewareFactoryStub, }; @@ -197,9 +191,9 @@ describe('MiddlewareModule', () => { instance, }), ); - sinon - .stub(stubContainer, 'getModuleByKey') - .callsFake(() => new Module(class {}, stubContainer)); + vi.spyOn(stubContainer, 'getModuleByKey').mockImplementation( + () => new Module(class {}, stubContainer), + ); middlewareModule['container'] = stubContainer; await middlewareModule.registerRouteMiddleware( @@ -209,7 +203,7 @@ describe('MiddlewareModule', () => { moduleKey, app, ); - expect(createMiddlewareFactoryStub.calledOnce).to.be.true; + expect(createMiddlewareFactoryStub).toHaveBeenCalledOnce(); }); it('should insert the expected middleware definition', async () => { @@ -224,9 +218,9 @@ describe('MiddlewareModule', () => { instance, name: TestMiddleware.name, }); - const createMiddlewareFactoryStub = sinon - .stub() - .callsFake(() => () => null); + const createMiddlewareFactoryStub = vi + .fn() + .mockImplementation(() => () => null); const app = { createMiddlewareFactory: createMiddlewareFactoryStub, }; @@ -241,12 +235,12 @@ describe('MiddlewareModule', () => { container .getMiddlewareCollection(moduleKey) .set(TestMiddleware, instanceWrapper); - sinon - .stub(stubContainer, 'getModuleByKey') - .callsFake(() => new Module(class {}, stubContainer)); + vi.spyOn(stubContainer, 'getModuleByKey').mockImplementation( + () => new Module(class {}, stubContainer), + ); middlewareModule['container'] = stubContainer; - const insertEntrypointDefinitionSpy = sinon.spy( + const insertEntrypointDefinitionSpy = vi.spyOn( graphInspector, 'insertEntrypointDefinition', ); @@ -259,9 +253,9 @@ describe('MiddlewareModule', () => { app, ); - expect(createMiddlewareFactoryStub.calledOnce).to.be.true; - expect( - insertEntrypointDefinitionSpy.calledWith({ + expect(createMiddlewareFactoryStub).toHaveBeenCalledOnce(); + expect(insertEntrypointDefinitionSpy).toHaveBeenCalledWith( + { type: 'middleware', methodName: 'use', className: instanceWrapper.name, @@ -272,8 +266,9 @@ describe('MiddlewareModule', () => { requestMethod: 'ALL', version: undefined, } as any, - }), - ).to.be.true; + }, + expect.any(String), + ); }); }); }); diff --git a/packages/core/test/middleware/resolver.spec.ts b/packages/core/test/middleware/resolver.spec.ts index 3641da2d07a..7d227b3f0ae 100644 --- a/packages/core/test/middleware/resolver.spec.ts +++ b/packages/core/test/middleware/resolver.spec.ts @@ -1,11 +1,9 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Injectable } from '../../../common'; -import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface'; -import { NestContainer } from '../../injector'; -import { Injector } from '../../injector/injector'; -import { MiddlewareContainer } from '../../middleware/container'; -import { MiddlewareResolver } from '../../middleware/resolver'; +import { Injectable } from '../../../common/index.js'; +import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface.js'; +import { NestContainer } from '../../injector/index.js'; +import { Injector } from '../../injector/injector.js'; +import { MiddlewareContainer } from '../../middleware/container.js'; +import { MiddlewareResolver } from '../../middleware/resolver.js'; describe('MiddlewareResolver', () => { @Injectable() @@ -15,17 +13,17 @@ describe('MiddlewareResolver', () => { let resolver: MiddlewareResolver; let container: MiddlewareContainer; - let mockContainer: sinon.SinonMock; beforeEach(() => { const injector = new Injector(); container = new MiddlewareContainer(new NestContainer()); resolver = new MiddlewareResolver(container, injector); - mockContainer = sinon.mock(container); }); it('should resolve middleware instances from container', async () => { - const loadMiddleware = sinon.stub(resolver['injector'], 'loadMiddleware'); + const loadMiddleware = vi + .spyOn(resolver['injector'], 'loadMiddleware') + .mockImplementation(() => ({}) as any); const middleware = new Map(); const wrapper = { instance: { metatype: {} }, @@ -34,13 +32,16 @@ describe('MiddlewareResolver', () => { middleware.set('TestMiddleware', wrapper); const module = { metatype: { name: '' } } as any; - mockContainer.expects('getMiddlewareCollection').returns(middleware); + vi.spyOn(container, 'getMiddlewareCollection').mockReturnValue(middleware); await resolver.resolveInstances(module, null!); - expect(loadMiddleware.callCount).to.be.equal(middleware.size); - expect(loadMiddleware.calledWith(wrapper as any, middleware, module)).to.be - .true; + expect(loadMiddleware.mock.calls.length).toBe(middleware.size); + expect(loadMiddleware).toHaveBeenCalledWith( + wrapper as any, + middleware, + module, + ); - loadMiddleware.restore(); + loadMiddleware.mockRestore?.(); }); }); diff --git a/packages/core/test/middleware/route-info-path-extractor.spec.ts b/packages/core/test/middleware/route-info-path-extractor.spec.ts index 62bfb5fc8d7..7e2ed75be5b 100644 --- a/packages/core/test/middleware/route-info-path-extractor.spec.ts +++ b/packages/core/test/middleware/route-info-path-extractor.spec.ts @@ -1,8 +1,7 @@ import { RequestMethod, VersioningType } from '@nestjs/common'; import { ApplicationConfig } from '@nestjs/core'; -import { mapToExcludeRoute } from '@nestjs/core/middleware/utils'; -import { expect } from 'chai'; -import { RouteInfoPathExtractor } from './../../middleware/route-info-path-extractor'; +import { mapToExcludeRoute } from '@nestjs/core/middleware/utils.js'; +import { RouteInfoPathExtractor } from './../../middleware/route-info-path-extractor.js'; describe('RouteInfoPathExtractor', () => { describe('extractPathsFrom', () => { @@ -23,7 +22,7 @@ describe('RouteInfoPathExtractor', () => { path: '*', method: RequestMethod.ALL, }), - ).to.eql(['/*']); + ).toEqual(['/*']); expect( routeInfoPathExtractor.extractPathsFrom({ @@ -31,7 +30,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/v1$', '/v1/*']); + ).toEqual(['/v1$', '/v1/*']); }); it(`should return correct paths when set global prefix`, () => { @@ -42,7 +41,7 @@ describe('RouteInfoPathExtractor', () => { path: '*', method: RequestMethod.ALL, }), - ).to.eql(['/api$', '/api/*']); + ).toEqual(['/api$', '/api/*']); expect( routeInfoPathExtractor.extractPathsFrom({ @@ -50,7 +49,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/api/v1$', '/api/v1/*']); + ).toEqual(['/api/v1$', '/api/v1/*']); }); it(`should return correct paths when set global prefix and global prefix options`, () => { @@ -66,7 +65,7 @@ describe('RouteInfoPathExtractor', () => { path: '*', method: RequestMethod.ALL, }), - ).to.eql(['/api$', '/api/*', '/foo']); + ).toEqual(['/api$', '/api/*', '/foo']); expect( routeInfoPathExtractor.extractPathsFrom({ @@ -74,7 +73,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/api/v1$', '/api/v1/*', '/v1/foo']); + ).toEqual(['/api/v1$', '/api/v1/*', '/v1/foo']); expect( routeInfoPathExtractor.extractPathsFrom({ @@ -82,7 +81,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/v1/foo']); + ).toEqual(['/v1/foo']); expect( routeInfoPathExtractor.extractPathsFrom({ @@ -90,7 +89,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/api/v1/bar']); + ).toEqual(['/api/v1/bar']); }); }); @@ -112,7 +111,7 @@ describe('RouteInfoPathExtractor', () => { path: '*', method: RequestMethod.ALL, }), - ).to.eql(['/*']); + ).toEqual(['/*']); expect( routeInfoPathExtractor.extractPathFrom({ @@ -120,7 +119,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/v1/*']); + ).toEqual(['/v1/*']); }); it(`should return correct path when set global prefix`, () => { @@ -131,7 +130,7 @@ describe('RouteInfoPathExtractor', () => { path: '*', method: RequestMethod.ALL, }), - ).to.eql(['/*']); + ).toEqual(['/*']); expect( routeInfoPathExtractor.extractPathFrom({ @@ -139,7 +138,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/api/v1/*']); + ).toEqual(['/api/v1/*']); }); it(`should return correct path when set global prefix and global prefix options`, () => { @@ -155,7 +154,7 @@ describe('RouteInfoPathExtractor', () => { path: '*', method: RequestMethod.ALL, }), - ).to.eql(['/*']); + ).toEqual(['/*']); expect( routeInfoPathExtractor.extractPathFrom({ @@ -163,7 +162,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/api/v1/*']); + ).toEqual(['/api/v1/*']); expect( routeInfoPathExtractor.extractPathFrom({ @@ -171,7 +170,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/v1/foo']); + ).toEqual(['/v1/foo']); expect( routeInfoPathExtractor.extractPathFrom({ @@ -179,7 +178,7 @@ describe('RouteInfoPathExtractor', () => { method: RequestMethod.ALL, version: '1', }), - ).to.eql(['/api/v1/bar']); + ).toEqual(['/api/v1/bar']); }); }); }); diff --git a/packages/core/test/middleware/routes-mapper.spec.ts b/packages/core/test/middleware/routes-mapper.spec.ts index e64a17d473d..d7dac93e23b 100644 --- a/packages/core/test/middleware/routes-mapper.spec.ts +++ b/packages/core/test/middleware/routes-mapper.spec.ts @@ -1,15 +1,14 @@ -import { expect } from 'chai'; -import { Version, VersioningType } from '../../../common'; -import { Controller } from '../../../common/decorators/core/controller.decorator'; +import { Version, VersioningType } from '../../../common/index.js'; +import { Controller } from '../../../common/decorators/core/controller.decorator.js'; import { Get, RequestMapping, -} from '../../../common/decorators/http/request-mapping.decorator'; -import { RequestMethod } from '../../../common/enums/request-method.enum'; -import { MiddlewareConfiguration } from '../../../common/interfaces'; -import { ApplicationConfig } from '../../application-config'; -import { NestContainer } from '../../injector/container'; -import { RoutesMapper } from '../../middleware/routes-mapper'; +} from '../../../common/decorators/http/request-mapping.decorator.js'; +import { RequestMethod } from '../../../common/enums/request-method.enum.js'; +import { MiddlewareConfiguration } from '../../../common/interfaces/index.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { NestContainer } from '../../injector/container.js'; +import { RoutesMapper } from '../../middleware/routes-mapper.js'; describe('RoutesMapper', () => { @Controller('test') @@ -42,15 +41,15 @@ describe('RoutesMapper', () => { ], }; - expect(mapper.mapRouteToRouteInfo(config.forRoutes[0])).to.deep.equal([ + expect(mapper.mapRouteToRouteInfo(config.forRoutes[0])).toEqual([ { path: '/test', method: RequestMethod.GET }, ]); - expect(mapper.mapRouteToRouteInfo(config.forRoutes[1])).to.deep.equal([ + expect(mapper.mapRouteToRouteInfo(config.forRoutes[1])).toEqual([ { path: '/versioned', version: '1', method: RequestMethod.GET }, ]); - expect(mapper.mapRouteToRouteInfo(config.forRoutes[2])).to.deep.equal([ + expect(mapper.mapRouteToRouteInfo(config.forRoutes[2])).toEqual([ { path: '/test/test', method: RequestMethod.GET }, { path: '/test/another', method: RequestMethod.DELETE }, { path: '/test/versioned', method: RequestMethod.GET, version: '1' }, @@ -74,10 +73,10 @@ describe('RoutesMapper', () => { ], }; - expect(mapper.mapRouteToRouteInfo(config.forRoutes[0])).to.deep.equal([ + expect(mapper.mapRouteToRouteInfo(config.forRoutes[0])).toEqual([ { path: '/test', method: RequestMethod.GET }, ]); - expect(mapper.mapRouteToRouteInfo(config.forRoutes[1])).to.deep.equal([ + expect(mapper.mapRouteToRouteInfo(config.forRoutes[1])).toEqual([ { path: '/test/test', method: RequestMethod.GET }, { path: '/test/another', method: RequestMethod.DELETE }, { path: '/test2/test', method: RequestMethod.GET }, @@ -113,18 +112,16 @@ describe('RoutesMapper', () => { } it('should map a versioned controller to the corresponding route info objects (single version)', () => { - expect(mapper.mapRouteToRouteInfo(VersionedController)).to.deep.equal([ + expect(mapper.mapRouteToRouteInfo(VersionedController)).toEqual([ { path: '/versioned/', version: '1', method: RequestMethod.GET }, { path: '/versioned/override', version: '2', method: RequestMethod.GET }, ]); }); it('should map a versioned controller to the corresponding route info objects (multiple versions)', () => { - expect(mapper.mapRouteToRouteInfo(MultipleVersionController)).to.deep.equal( - [ - { path: '/multiple', version: '1', method: RequestMethod.GET }, - { path: '/multiple', version: '2', method: RequestMethod.GET }, - ], - ); + expect(mapper.mapRouteToRouteInfo(MultipleVersionController)).toEqual([ + { path: '/multiple', version: '1', method: RequestMethod.GET }, + { path: '/multiple', version: '2', method: RequestMethod.GET }, + ]); }); }); diff --git a/packages/core/test/middleware/utils.spec.ts b/packages/core/test/middleware/utils.spec.ts index df94e8a5b79..c73117db425 100644 --- a/packages/core/test/middleware/utils.spec.ts +++ b/packages/core/test/middleware/utils.spec.ts @@ -1,8 +1,6 @@ import { RequestMethod } from '@nestjs/common'; -import { addLeadingSlash } from '@nestjs/common/utils/shared.utils'; -import { expect } from 'chai'; +import { addLeadingSlash } from '@nestjs/common/utils/shared.utils.js'; import { pathToRegexp } from 'path-to-regexp'; -import * as sinon from 'sinon'; import { assignToken, filterMiddleware, @@ -10,8 +8,8 @@ import { isMiddlewareRouteExcluded, mapToClass, mapToExcludeRoute, -} from '../../middleware/utils'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +} from '../../middleware/utils.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('middleware utils', () => { const noopAdapter = new NoopHttpAdapter({}); @@ -26,7 +24,7 @@ describe('middleware utils', () => { path: 'bar', method: RequestMethod.GET, }; - expect(mapToExcludeRoute([stringRoute, routeInfo])).to.eql([ + expect(mapToExcludeRoute([stringRoute, routeInfo])).toEqual([ { path: stringRoute, requestMethod: RequestMethod.ALL, @@ -46,7 +44,7 @@ describe('middleware utils', () => { middleware = [Test, fnMiddleware, undefined, null]; }); it('should return filtered middleware', () => { - expect(filterMiddleware(middleware, [], noopAdapter)).to.have.length(2); + expect(filterMiddleware(middleware, [], noopAdapter)).toHaveLength(2); }); }); describe('mapToClass', () => { @@ -54,7 +52,7 @@ describe('middleware utils', () => { describe('when there is no excluded routes', () => { it('should return an identity', () => { const type = mapToClass(Test, [], noopAdapter); - expect(type).to.eql(Test); + expect(type).toEqual(Test); }); }); describe('when there are excluded routes', () => { @@ -64,52 +62,52 @@ describe('middleware utils', () => { mapToExcludeRoute([{ path: '*', method: RequestMethod.ALL }]), noopAdapter, ); - expect(type).to.not.eql(Test); - expect(type.name).to.eql(Test.name); + expect(type).not.toEqual(Test); + expect(type.name).toEqual(Test.name); }); }); }); describe('when middleware is a function', () => { it('should return a metatype', () => { const metatype = mapToClass(fnMiddleware, [], noopAdapter); - expect(metatype).to.not.eql(fnMiddleware); + expect(metatype).not.toEqual(fnMiddleware); }); it('should define a `use` method', () => { const metatype = mapToClass(fnMiddleware, [], noopAdapter); - expect(new metatype().use).to.exist; + expect(new metatype().use).toBeDefined(); }); it('should encapsulate a function', () => { - const spy = sinon.spy(); + const spy = vi.fn(); const metatype = mapToClass(spy, [], noopAdapter); new metatype().use(); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); }); describe('isMiddlewareClass', () => { describe('when middleware is a class', () => { it('should returns true', () => { - expect(isMiddlewareClass(Test)).to.be.true; + expect(isMiddlewareClass(Test)).toBe(true); }); }); describe('when middleware is a function', () => { it('should returns false', () => { - expect(isMiddlewareClass(fnMiddleware)).to.be.false; + expect(isMiddlewareClass(fnMiddleware)).toBe(false); }); }); }); describe('assignToken', () => { - describe('should define `name` property on metatype', () => { + it('should define `name` property on metatype', () => { const AnonymousType = class {}; assignToken(AnonymousType); - expect(AnonymousType.name).to.exist; + expect(AnonymousType.name).toBeDefined(); }); - describe('should use passed token as `name`', () => { + it('should use passed token as `name`', () => { const AnonymousType = class {}; const token = 'token'; assignToken(AnonymousType, token); - expect(AnonymousType.name).to.eq(token); + expect(AnonymousType.name).toBe(token); }); }); @@ -118,8 +116,8 @@ describe('middleware utils', () => { beforeEach(() => { adapter = new NoopHttpAdapter({}); - sinon.stub(adapter, 'getRequestMethod').callsFake(() => 'GET'); - sinon.stub(adapter, 'getRequestUrl').callsFake(() => '/cats/3'); + vi.spyOn(adapter, 'getRequestMethod').mockImplementation(() => 'GET'); + vi.spyOn(adapter, 'getRequestUrl').mockImplementation(() => '/cats/3'); }); describe('when route is excluded (new syntax *path)', () => { const path = '/cats/*path'; @@ -130,8 +128,9 @@ describe('middleware utils', () => { }, ]); it('should return true', () => { - expect(isMiddlewareRouteExcluded({}, excludedRoutes, adapter)).to.be - .true; + expect(isMiddlewareRouteExcluded({}, excludedRoutes, adapter)).toBe( + true, + ); }); }); describe('when route is excluded (legacy syntax (.*))', () => { @@ -143,13 +142,14 @@ describe('middleware utils', () => { }, ]); it('should return true', () => { - expect(isMiddlewareRouteExcluded({}, excludedRoutes, adapter)).to.be - .true; + expect(isMiddlewareRouteExcluded({}, excludedRoutes, adapter)).toBe( + true, + ); }); }); describe('when route is not excluded', () => { it('should return false', () => { - expect(isMiddlewareRouteExcluded({}, [], adapter)).to.be.false; + expect(isMiddlewareRouteExcluded({}, [], adapter)).toBe(false); }); }); }); diff --git a/packages/core/test/nest-application-context.spec.ts b/packages/core/test/nest-application-context.spec.ts index e49d90d48e7..7117894cd4f 100644 --- a/packages/core/test/nest-application-context.spec.ts +++ b/packages/core/test/nest-application-context.spec.ts @@ -1,13 +1,10 @@ import { Injectable, InjectionToken, Provider, Scope } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { setTimeout } from 'timers/promises'; -import { ContextIdFactory } from '../helpers/context-id-factory'; -import { NestContainer } from '../injector/container'; -import { Injector } from '../injector/injector'; -import { InstanceLoader } from '../injector/instance-loader'; -import { GraphInspector } from '../inspector/graph-inspector'; -import { NestApplicationContext } from '../nest-application-context'; +import { ContextIdFactory } from '../helpers/context-id-factory.js'; +import { NestContainer } from '../injector/container.js'; +import { Injector } from '../injector/injector.js'; +import { InstanceLoader } from '../injector/instance-loader.js'; +import { GraphInspector } from '../inspector/graph-inspector.js'; +import { NestApplicationContext } from '../nest-application-context.js'; describe('NestApplicationContext', () => { class A {} @@ -87,9 +84,9 @@ describe('NestApplicationContext', () => { process.kill(process.pid, signal); }); - const hookStub = sinon - .stub(applicationContext as any, 'callShutdownHook') - .callsFake(async () => { + const hookStub = vi + .spyOn(applicationContext as any, 'callShutdownHook') + .mockImplementation(async () => { // run some async code await new Promise(resolve => setImmediate(() => resolve(undefined))); if (processUp) { @@ -98,28 +95,33 @@ describe('NestApplicationContext', () => { }); process.kill(process.pid, signal); await waitProcessDown; - hookStub.restore(); - expect(processUp).to.be.false; - expect(promisesResolved).to.be.true; + hookStub.mockRestore(); + expect(processUp).toBe(false); + expect(promisesResolved).toBe(true); }); it('should defer shutdown until all init hooks are resolved', async () => { - const clock = sinon.useFakeTimers({ + const clock = vi.useFakeTimers({ toFake: ['setTimeout'], }); const signal = 'SIGTERM'; - const onModuleInitStub = sinon.stub(); - const onApplicationShutdownStub = sinon.stub(); + const onModuleInitStub = vi.fn(); + const onApplicationShutdownStub = vi.fn(); + + // Use global setTimeout wrapped in a Promise so sinon fake timers + // can intercept it (timers/promises.setTimeout is not fakeable in ESM). + const delay = (ms: number) => + new Promise(resolve => globalThis.setTimeout(resolve, ms)); class B { async onModuleInit() { - await setTimeout(5000); + await delay(5000); onModuleInitStub(); } async onApplicationShutdown() { - await setTimeout(1000); + await delay(1000); onApplicationShutdownStub(); } } @@ -135,74 +137,82 @@ describe('NestApplicationContext', () => { process.on(signal, ignoreProcessSignal); const deferredShutdown = async () => { - await setTimeout(1); + await delay(1); process.kill(process.pid, signal); }; void Promise.all([applicationContext.init(), deferredShutdown()]); - await clock.nextAsync(); - expect(onModuleInitStub.called).to.be.false; - expect(onApplicationShutdownStub.called).to.be.false; + await vi.advanceTimersByTimeAsync(1); + expect(onModuleInitStub).not.toHaveBeenCalled(); + expect(onApplicationShutdownStub).not.toHaveBeenCalled(); - await clock.nextAsync(); - expect(onModuleInitStub.called).to.be.true; - expect(onApplicationShutdownStub.called).to.be.false; + await vi.advanceTimersByTimeAsync(5000); + expect(onModuleInitStub).toHaveBeenCalled(); + expect(onApplicationShutdownStub).not.toHaveBeenCalled(); - await clock.nextAsync(); - expect(onModuleInitStub.called).to.be.true; - expect(onApplicationShutdownStub.called).to.be.true; + await vi.advanceTimersByTimeAsync(1000); + expect(onModuleInitStub).toHaveBeenCalled(); + expect(onApplicationShutdownStub).toHaveBeenCalled(); - clock.restore(); + vi.useRealTimers(); }); it('should use process.exit when useProcessExit option is enabled', async () => { const signal = 'SIGTERM'; const applicationContext = await testHelper(A, Scope.DEFAULT); - const processExitStub = sinon.stub(process, 'exit'); - const processKillStub = sinon.stub(process, 'kill'); + const processExitStub = vi + .spyOn(process, 'exit') + .mockImplementation(() => ({}) as any); + const processKillStub = vi + .spyOn(process, 'kill') + .mockImplementation(() => ({}) as any); applicationContext.enableShutdownHooks([signal], { useProcessExit: true, }); - const hookStub = sinon - .stub(applicationContext as any, 'callShutdownHook') - .callsFake(async () => undefined); + const hookStub = vi + .spyOn(applicationContext as any, 'callShutdownHook') + .mockImplementation(async () => undefined); const shutdownCleanupRef = applicationContext['shutdownCleanupRef']!; await shutdownCleanupRef(signal); - hookStub.restore(); - processExitStub.restore(); - processKillStub.restore(); + expect(processExitStub).toHaveBeenCalledWith(0); + expect(processKillStub).not.toHaveBeenCalled(); - expect(processExitStub.calledOnceWith(0)).to.be.true; - expect(processKillStub.called).to.be.false; + hookStub.mockRestore(); + processExitStub.mockRestore(); + processKillStub.mockRestore(); }); it('should use process.kill when useProcessExit option is not enabled', async () => { const signal = 'SIGTERM'; const applicationContext = await testHelper(A, Scope.DEFAULT); - const processExitStub = sinon.stub(process, 'exit'); - const processKillStub = sinon.stub(process, 'kill'); + const processExitStub = vi + .spyOn(process, 'exit') + .mockImplementation(() => ({}) as any); + const processKillStub = vi + .spyOn(process, 'kill') + .mockImplementation(() => ({}) as any); applicationContext.enableShutdownHooks([signal]); - const hookStub = sinon - .stub(applicationContext as any, 'callShutdownHook') - .callsFake(async () => undefined); + const hookStub = vi + .spyOn(applicationContext as any, 'callShutdownHook') + .mockImplementation(async () => undefined); const shutdownCleanupRef = applicationContext['shutdownCleanupRef']!; await shutdownCleanupRef(signal); - hookStub.restore(); - processExitStub.restore(); - processKillStub.restore(); + expect(processKillStub).toHaveBeenCalledWith(process.pid, signal); + expect(processExitStub).not.toHaveBeenCalled(); - expect(processKillStub.calledOnceWith(process.pid, signal)).to.be.true; - expect(processExitStub.called).to.be.false; + hookStub.mockRestore(); + processExitStub.mockRestore(); + processKillStub.mockRestore(); }); }); @@ -215,9 +225,9 @@ describe('NestApplicationContext', () => { const a1: A = await applicationContext.get(key); const a2: A = await applicationContext.get(key); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).equal(a2); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).toBe(a2); }); it('should get value with string injection key', async () => { @@ -227,9 +237,9 @@ describe('NestApplicationContext', () => { const a1: A = await applicationContext.get(key); const a2: A = await applicationContext.get(key); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).equal(a2); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).toBe(a2); }); it('should get value with symbol injection key', async () => { @@ -239,9 +249,9 @@ describe('NestApplicationContext', () => { const a1: A = await applicationContext.get(key); const a2: A = await applicationContext.get(key); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).equal(a2); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).toBe(a2); }); }); @@ -250,21 +260,21 @@ describe('NestApplicationContext', () => { const key = A; const applicationContext = await testHelper(key, Scope.REQUEST); - expect(() => applicationContext.get(key)).to.be.throw; + expect(() => applicationContext.get(key)).toThrow(); }); it('should throw error when use string injection key', async () => { const key = 'KEY_A'; const applicationContext = await testHelper(key, Scope.REQUEST); - expect(() => applicationContext.get(key)).to.be.throw; + expect(() => applicationContext.get(key)).toThrow(); }); it('should throw error when use symbol injection key', async () => { const key = Symbol('KEY_A'); const applicationContext = await testHelper(key, Scope.REQUEST); - expect(() => applicationContext.get(key)).to.be.throw; + expect(() => applicationContext.get(key)).toThrow(); }); }); @@ -273,21 +283,21 @@ describe('NestApplicationContext', () => { const key = A; const applicationContext = await testHelper(key, Scope.TRANSIENT); - expect(() => applicationContext.get(key)).to.be.throw; + expect(() => applicationContext.get(key)).toThrow(); }); it('should throw error when use string injection key', async () => { const key = 'KEY_A'; const applicationContext = await testHelper(key, Scope.TRANSIENT); - expect(() => applicationContext.get(key)).to.be.throw; + expect(() => applicationContext.get(key)).toThrow(); }); it('should throw error when use symbol injection key', async () => { const key = Symbol('KEY_A'); const applicationContext = await testHelper(key, Scope.TRANSIENT); - expect(() => applicationContext.get(key)).to.be.throw; + expect(() => applicationContext.get(key)).toThrow(); }); }); }); @@ -301,9 +311,9 @@ describe('NestApplicationContext', () => { const a1: A = await applicationContext.resolve(key); const a2: A = await applicationContext.resolve(key); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).equal(a2); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).toBe(a2); }); it('should resolve value with string injection key', async () => { @@ -313,9 +323,9 @@ describe('NestApplicationContext', () => { const a1: A = await applicationContext.resolve(key); const a2: A = await applicationContext.resolve(key); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).equal(a2); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).toBe(a2); }); it('should resolve value with symbol injection key', async () => { @@ -325,9 +335,9 @@ describe('NestApplicationContext', () => { const a1: A = await applicationContext.resolve(key); const a2: A = await applicationContext.resolve(key); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).equal(a2); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).toBe(a2); }); }); @@ -341,10 +351,10 @@ describe('NestApplicationContext', () => { const a2: A = await applicationContext.resolve(key, contextId); const a3: A = await applicationContext.resolve(key, contextId); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).not.equal(a2); - expect(a2).equal(a3); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).not.toBe(a2); + expect(a2).toBe(a3); }); it('should resolve value with string injection key', async () => { @@ -356,10 +366,10 @@ describe('NestApplicationContext', () => { const a2: A = await applicationContext.resolve(key, contextId); const a3: A = await applicationContext.resolve(key, contextId); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).not.equal(a2); - expect(a2).equal(a3); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).not.toBe(a2); + expect(a2).toBe(a3); }); it('should resolve value with symbol injection key', async () => { @@ -371,10 +381,10 @@ describe('NestApplicationContext', () => { const a2: A = await applicationContext.resolve(key, contextId); const a3: A = await applicationContext.resolve(key, contextId); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).not.equal(a2); - expect(a2).equal(a3); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).not.toBe(a2); + expect(a2).toBe(a3); }); }); @@ -388,10 +398,10 @@ describe('NestApplicationContext', () => { const a2: A = await applicationContext.resolve(key, contextId); const a3: A = await applicationContext.resolve(key, contextId); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).not.equal(a2); - expect(a2).equal(a3); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).not.toBe(a2); + expect(a2).toBe(a3); }); it('should resolve value with string injection key', async () => { @@ -403,10 +413,10 @@ describe('NestApplicationContext', () => { const a2: A = await applicationContext.resolve(key, contextId); const a3: A = await applicationContext.resolve(key, contextId); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).not.equal(a2); - expect(a2).equal(a3); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).not.toBe(a2); + expect(a2).toBe(a3); }); it('should resolve value with symbol injection key', async () => { @@ -418,10 +428,10 @@ describe('NestApplicationContext', () => { const a2: A = await applicationContext.resolve(key, contextId); const a3: A = await applicationContext.resolve(key, contextId); - expect(a1).instanceOf(A); - expect(a2).instanceOf(A); - expect(a1).not.equal(a2); - expect(a2).equal(a3); + expect(a1).toBeInstanceOf(A); + expect(a2).toBeInstanceOf(A); + expect(a1).not.toBe(a2); + expect(a2).toBe(a3); }); }); }); @@ -454,7 +464,7 @@ describe('NestApplicationContext', () => { const appCtx = new NestApplicationContext(nestContainer); // With a non-static dependency tree, get() should refuse and instruct to use resolve() - expect(() => appCtx.get(Host)).to.throw(); + expect(() => appCtx.get(Host)).toThrow(); }); it('resolve() should instantiate when dependency tree is not static (request-scoped enhancer attached)', async () => { @@ -482,7 +492,7 @@ describe('NestApplicationContext', () => { const appCtx = new NestApplicationContext(nestContainer); const instance = await appCtx.resolve(Host); - expect(instance).instanceOf(Host); + expect(instance).toBeInstanceOf(Host); }); }); @@ -538,11 +548,11 @@ describe('NestApplicationContext', () => { each: true, }); - expect(instances).to.be.an('array'); - expect(instances).to.have.length(3); - expect(instances[0]).to.be.instanceOf(Service1); - expect(instances[1]).to.be.instanceOf(Service2); - expect(instances[2]).to.be.instanceOf(Service3); + expect(instances).toEqual(expect.any(Array)); + expect(instances).toHaveLength(3); + expect(instances[0]).toBeInstanceOf(Service1); + expect(instances[1]).toBeInstanceOf(Service2); + expect(instances[2]).toBeInstanceOf(Service3); }); }); }); diff --git a/packages/core/test/nest-application.spec.ts b/packages/core/test/nest-application.spec.ts index f74b3052cf2..abb2ef43d4e 100644 --- a/packages/core/test/nest-application.spec.ts +++ b/packages/core/test/nest-application.spec.ts @@ -1,22 +1,33 @@ import { RequestMethod } from '@nestjs/common'; -import { expect } from 'chai'; -import { ApplicationConfig } from '../application-config'; -import { NestContainer } from '../injector/container'; -import { GraphInspector } from '../inspector/graph-inspector'; -import { NestApplication } from '../nest-application'; -import { mapToExcludeRoute } from './../middleware/utils'; -import { NoopHttpAdapter } from './utils/noop-adapter.spec'; +import { loadPackage } from '@nestjs/common/utils/load-package.util.js'; +import * as microservicesPackage from '@nestjs/microservices'; import { MicroserviceOptions } from '@nestjs/microservices'; -import * as sinon from 'sinon'; +import { ApplicationConfig } from '../application-config.js'; +import { NestContainer } from '../injector/container.js'; +import { GraphInspector } from '../inspector/graph-inspector.js'; +import { NestApplication } from '../nest-application.js'; +import { mapToExcludeRoute } from './../middleware/utils.js'; +import { NoopHttpAdapter } from './utils/noop-adapter.js'; describe('NestApplication', () => { + beforeAll(async () => { + // Pre-populate the package cache so that connectMicroservice() + // can synchronously retrieve @nestjs/microservices via loadPackageCached. + // Use the already-imported module to avoid a slow dynamic import() on CI. + await loadPackage( + '@nestjs/microservices', + 'NestApplication tests', + () => microservicesPackage, + ); + }); + describe('Hybrid Application', () => { class Interceptor { public intercept(context, next) { return next(); } } - it('default should use new ApplicationConfig', () => { + it('default should use new ApplicationConfig', async () => { const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig); const instance = new NestApplication( @@ -30,14 +41,12 @@ describe('NestApplication', () => { const microservice = instance.connectMicroservice( {}, ); - expect((instance as any).config.getGlobalInterceptors().length).to.equal( - 1, - ); + expect((instance as any).config.getGlobalInterceptors().length).toBe(1); expect( (microservice as any).applicationConfig.getGlobalInterceptors().length, - ).to.equal(0); + ).toBe(0); }); - it('should inherit existing ApplicationConfig', () => { + it('should inherit existing ApplicationConfig', async () => { const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig); const instance = new NestApplication( @@ -52,15 +61,13 @@ describe('NestApplication', () => { {}, { inheritAppConfig: true }, ); - expect((instance as any).config.getGlobalInterceptors().length).to.equal( - 1, - ); + expect((instance as any).config.getGlobalInterceptors().length).toBe(1); expect( (microservice as any).applicationConfig.getGlobalInterceptors().length, - ).to.equal(1); + ).toBe(1); }); - it('should immediately initialize microservice by default', () => { + it('should immediately initialize microservice by default', async () => { const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig); const instance = new NestApplication( @@ -76,11 +83,11 @@ describe('NestApplication', () => { {}, ); - expect((microservice as any).isInitialized).to.be.true; - expect((microservice as any).wasInitHookCalled).to.be.true; + expect((microservice as any).isInitialized).toBe(true); + expect((microservice as any).wasInitHookCalled).toBe(true); }); - it('should defer microservice initialization when deferInitialization is true', () => { + it('should defer microservice initialization when deferInitialization is true', async () => { const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig); const instance = new NestApplication( @@ -96,8 +103,8 @@ describe('NestApplication', () => { { deferInitialization: true }, ); - expect((microservice as any).isInitialized).to.be.false; - expect((microservice as any).wasInitHookCalled).to.be.false; + expect((microservice as any).isInitialized).toBe(false); + expect((microservice as any).wasInitHookCalled).toBe(false); }); }); describe('Global Prefix', () => { @@ -115,7 +122,7 @@ describe('NestApplication', () => { instance.setGlobalPrefix('api', { exclude: excludeRoute, }); - expect(applicationConfig.getGlobalPrefixOptions()).to.eql({ + expect(applicationConfig.getGlobalPrefixOptions()).toEqual({ exclude: mapToExcludeRoute(excludeRoute), }); }); @@ -123,7 +130,7 @@ describe('NestApplication', () => { describe('Double initialization', () => { it('should initialize application only once', async () => { const noopHttpAdapter = new NoopHttpAdapter({}); - const httpAdapterSpy = sinon.spy(noopHttpAdapter); + (noopHttpAdapter as any).init = vi.fn(); const applicationConfig = new ApplicationConfig(); @@ -141,7 +148,7 @@ describe('NestApplication', () => { await instance.init(); await instance.init(); - expect(httpAdapterSpy.init.calledOnce).to.be.true; + expect((noopHttpAdapter as any).init).toHaveBeenCalledOnce(); }); }); }); diff --git a/packages/core/test/pipes/params-token-factory.spec.ts b/packages/core/test/pipes/params-token-factory.spec.ts index 6bc83aadd07..214eee9a487 100644 --- a/packages/core/test/pipes/params-token-factory.spec.ts +++ b/packages/core/test/pipes/params-token-factory.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum'; -import { ParamsTokenFactory } from '../../pipes/params-token-factory'; +import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum.js'; +import { ParamsTokenFactory } from '../../pipes/params-token-factory.js'; describe('ParamsTokenFactory', () => { let factory: ParamsTokenFactory; @@ -11,28 +10,28 @@ describe('ParamsTokenFactory', () => { describe('when key is', () => { describe(`RouteParamtypes.BODY`, () => { it('should return body object', () => { - expect(factory.exchangeEnumForString(RouteParamtypes.BODY)).to.be.eql( + expect(factory.exchangeEnumForString(RouteParamtypes.BODY)).toEqual( 'body', ); }); }); describe(`RouteParamtypes.QUERY`, () => { it('should return query object', () => { - expect( - factory.exchangeEnumForString(RouteParamtypes.QUERY), - ).to.be.eql('query'); + expect(factory.exchangeEnumForString(RouteParamtypes.QUERY)).toEqual( + 'query', + ); }); }); describe(`RouteParamtypes.PARAM`, () => { it('should return params object', () => { - expect( - factory.exchangeEnumForString(RouteParamtypes.PARAM), - ).to.be.eql('param'); + expect(factory.exchangeEnumForString(RouteParamtypes.PARAM)).toEqual( + 'param', + ); }); }); describe('not available', () => { it('should return "custom"', () => { - expect(factory.exchangeEnumForString(-1 as any)).to.be.eql('custom'); + expect(factory.exchangeEnumForString(-1 as any)).toEqual('custom'); }); }); }); diff --git a/packages/core/test/pipes/pipes-consumer.spec.ts b/packages/core/test/pipes/pipes-consumer.spec.ts index bae8d33899d..cc885dfc2c0 100644 --- a/packages/core/test/pipes/pipes-consumer.spec.ts +++ b/packages/core/test/pipes/pipes-consumer.spec.ts @@ -1,7 +1,5 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum'; -import { PipesConsumer } from '../../pipes/pipes-consumer'; +import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum.js'; +import { PipesConsumer } from '../../pipes/pipes-consumer.js'; const createPipe = (transform: Function) => ({ transform }); @@ -18,33 +16,96 @@ describe('PipesConsumer', () => { ((metatype = {}), (type = RouteParamtypes.QUERY)); stringifiedType = 'query'; transforms = [ - createPipe(sinon.stub().callsFake(val => val + 1)), - createPipe(sinon.stub().callsFake(val => Promise.resolve(val + 1))), - createPipe(sinon.stub().callsFake(val => val + 1)), + createPipe(vi.fn().mockImplementation(val => val + 1)), + createPipe(vi.fn().mockImplementation(val => Promise.resolve(val + 1))), + createPipe(vi.fn().mockImplementation(val => val + 1)), ]; }); - it('should call all transform functions', done => { - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - consumer.apply(value, { metatype, type, data }, transforms).then(() => { - expect( - transforms.reduce( - (prev, next) => prev && next.transform.called, - true, - ), - ).to.be.true; + it('should call all transform functions', () => + new Promise(done => { + /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + consumer.apply(value, { metatype, type, data }, transforms).then(() => { + expect( + transforms.reduce( + (prev, next) => prev && next.transform.mock.calls.length > 0, + true, + ), + ).toBe(true); - done(); - }); - }); - it('should return expected result', done => { - const expectedResult = 3; - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - consumer - .apply(value, { metatype, type, data }, transforms) - .then(result => { - expect(result).to.be.eql(expectedResult); done(); }); + })); + it('should return expected result', () => + new Promise(done => { + const expectedResult = 3; + /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + consumer + .apply(value, { metatype, type, data }, transforms) + .then(result => { + expect(result).toEqual(expectedResult); + done(); + }); + })); + + describe('schema propagation', () => { + it('should pass schema to each pipe transform', async () => { + const mockSchema = { + '~standard': { + version: 1 as const, + vendor: 'test', + validate: (v: unknown) => ({ value: v }), + }, + }; + const receivedMetadata: any[] = []; + const pipesWithSchema = [ + createPipe( + vi.fn().mockImplementation((val, metadata) => { + receivedMetadata.push(metadata); + return val; + }), + ), + createPipe( + vi.fn().mockImplementation((val, metadata) => { + receivedMetadata.push(metadata); + return val; + }), + ), + ]; + + await consumer.apply( + 'testValue', + { metatype: String, type, data: 'testData', schema: mockSchema }, + pipesWithSchema as any, + ); + + expect(receivedMetadata).toHaveLength(2); + for (const metadata of receivedMetadata) { + expect(metadata.schema).toBe(mockSchema); + expect(metadata.data).toBe('testData'); + expect(metadata.type).toBe('query'); + } + }); + + it('should work without schema (schema is undefined)', async () => { + const receivedMetadata: any[] = []; + const pipes = [ + createPipe( + vi.fn().mockImplementation((val, metadata) => { + receivedMetadata.push(metadata); + return val; + }), + ), + ]; + + await consumer.apply( + 'testValue', + { metatype: String, type, data: 'testData' }, + pipes as any, + ); + + expect(receivedMetadata).toHaveLength(1); + expect(receivedMetadata[0].schema).toBeUndefined(); + }); }); }); }); diff --git a/packages/core/test/pipes/pipes-context-creator.spec.ts b/packages/core/test/pipes/pipes-context-creator.spec.ts index 63b7cc0c46f..c23c942b9ce 100644 --- a/packages/core/test/pipes/pipes-context-creator.spec.ts +++ b/packages/core/test/pipes/pipes-context-creator.spec.ts @@ -1,9 +1,7 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ApplicationConfig } from '../../application-config'; -import { NestContainer } from '../../injector/container'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { PipesContextCreator } from '../../pipes/pipes-context-creator'; +import { ApplicationConfig } from '../../application-config.js'; +import { NestContainer } from '../../injector/container.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { PipesContextCreator } from '../../pipes/pipes-context-creator.js'; class Pipe {} @@ -20,15 +18,15 @@ describe('PipesContextCreator', () => { describe('createConcreteContext', () => { describe('when metadata is empty or undefined', () => { it('should return empty array', () => { - expect(creator.createConcreteContext(undefined!)).to.be.deep.equal([]); - expect(creator.createConcreteContext([])).to.be.deep.equal([]); + expect(creator.createConcreteContext(undefined!)).toEqual([]); + expect(creator.createConcreteContext([])).toEqual([]); }); }); describe('when metadata is not empty or undefined', () => { const metadata = [null, {}, { transform: () => ({}) }]; it('should return expected array', () => { const transforms = creator.createConcreteContext(metadata as any); - expect(transforms).to.have.length(1); + expect(transforms).toHaveLength(1); }); }); }); @@ -36,7 +34,7 @@ describe('PipesContextCreator', () => { describe('when param is an object', () => { it('should return instance', () => { const instance = { transform: () => null }; - expect(creator.getPipeInstance(instance)).to.be.eql(instance); + expect(creator.getPipeInstance(instance)).toEqual(instance); }); }); describe('when param is a constructor', () => { @@ -45,12 +43,16 @@ describe('PipesContextCreator', () => { instance: 'test', getInstanceByContextId: () => wrapper, } as any; - sinon.stub(creator, 'getInstanceByMetatype').callsFake(() => wrapper); - expect(creator.getPipeInstance(Pipe)).to.be.eql(wrapper.instance); + vi.spyOn(creator, 'getInstanceByMetatype').mockImplementation( + () => wrapper, + ); + expect(creator.getPipeInstance(Pipe)).toEqual(wrapper.instance); }); it('should return null', () => { - sinon.stub(creator, 'getInstanceByMetatype').callsFake(() => null!); - expect(creator.getPipeInstance(Pipe)).to.be.eql(null); + vi.spyOn(creator, 'getInstanceByMetatype').mockImplementation( + () => null!, + ); + expect(creator.getPipeInstance(Pipe)).toEqual(null); }); }); }); @@ -59,7 +61,7 @@ describe('PipesContextCreator', () => { describe('when "moduleContext" is nil', () => { it('should return undefined', () => { (creator as any).moduleContext = undefined; - expect(creator.getInstanceByMetatype(null!)).to.be.undefined; + expect(creator.getInstanceByMetatype(null!)).toBeUndefined(); }); }); describe('when "moduleContext" is not nil', () => { @@ -69,8 +71,10 @@ describe('PipesContextCreator', () => { describe('and when module exists', () => { it('should return undefined', () => { - sinon.stub(container.getModules(), 'get').callsFake(() => undefined); - expect(creator.getInstanceByMetatype(null!)).to.be.undefined; + vi.spyOn(container.getModules(), 'get').mockImplementation( + () => undefined, + ); + expect(creator.getInstanceByMetatype(null!)).toBeUndefined(); }); }); @@ -78,10 +82,10 @@ describe('PipesContextCreator', () => { it('should return instance', () => { const instance = { test: true }; const module = { injectables: { get: () => instance } }; - sinon - .stub(container.getModules(), 'get') - .callsFake(() => module as any); - expect(creator.getInstanceByMetatype(class Test {})).to.be.eql( + vi.spyOn(container.getModules(), 'get').mockImplementation( + () => module as any, + ); + expect(creator.getInstanceByMetatype(class Test {})).toEqual( instance, ); }); @@ -93,7 +97,7 @@ describe('PipesContextCreator', () => { describe('when contextId is static and inquirerId is nil', () => { it('should return global pipes', () => { const expectedResult = applicationConfig.getGlobalPipes(); - expect(creator.getGlobalMetadata()).to.be.equal(expectedResult); + expect(creator.getGlobalMetadata()).toBe(expectedResult); }); }); describe('otherwise', () => { @@ -103,19 +107,18 @@ describe('PipesContextCreator', () => { const instance = 'request-scoped'; const scopedPipeWrappers = [instanceWrapper]; - sinon - .stub(applicationConfig, 'getGlobalPipes') - .callsFake(() => globalPipes); - sinon - .stub(applicationConfig, 'getGlobalRequestPipes') - .callsFake(() => scopedPipeWrappers); - sinon - .stub(instanceWrapper, 'getInstanceByContextId') - .callsFake(() => ({ instance }) as any); + vi.spyOn(applicationConfig, 'getGlobalPipes').mockImplementation( + () => globalPipes, + ); + vi.spyOn(applicationConfig, 'getGlobalRequestPipes').mockImplementation( + () => scopedPipeWrappers, + ); + vi.spyOn(instanceWrapper, 'getInstanceByContextId').mockImplementation( + () => ({ instance }) as any, + ); - expect(creator.getGlobalMetadata({ id: 3 })).to.contains( - instance, - ...globalPipes, + expect(creator.getGlobalMetadata({ id: 3 })).toEqual( + expect.arrayContaining([instance, ...globalPipes]), ); }); }); diff --git a/packages/core/test/repl/assign-to-object.util.spec.ts b/packages/core/test/repl/assign-to-object.util.spec.ts index 0a7a58bf5d1..ef01ee50c26 100644 --- a/packages/core/test/repl/assign-to-object.util.spec.ts +++ b/packages/core/test/repl/assign-to-object.util.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { assignToObject } from '../../repl/assign-to-object.util'; +import { assignToObject } from '../../repl/assign-to-object.util.js'; describe('assignToObject', () => { it('should copy all enumerable properties and their descriptors', () => { @@ -20,13 +19,13 @@ describe('assignToObject', () => { assignToObject(targetObj, sourceObj); - expect(Object.getOwnPropertyDescriptor(targetObj, 'foo')).to.be.eql({ + expect(Object.getOwnPropertyDescriptor(targetObj, 'foo')).toEqual({ value: 123, configurable: true, enumerable: true, writable: true, }); - expect(Object.getOwnPropertyDescriptor(targetObj, 'bar')).to.be.eql({ + expect(Object.getOwnPropertyDescriptor(targetObj, 'bar')).toEqual({ value: 456, configurable: true, enumerable: true, diff --git a/packages/core/test/repl/native-functions/debug-repl-fn.spec.ts b/packages/core/test/repl/native-functions/debug-repl-fn.spec.ts index cfda9b51a26..25e0efcba33 100644 --- a/packages/core/test/repl/native-functions/debug-repl-fn.spec.ts +++ b/packages/core/test/repl/native-functions/debug-repl-fn.spec.ts @@ -1,9 +1,7 @@ -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { NestContainer } from '../../../injector/container'; -import { DebugReplFn } from '../../../repl/native-functions'; -import { ReplContext } from '../../../repl/repl-context'; +import { clc } from '@nestjs/common/utils/cli-colors.util.js'; +import { NestContainer } from '../../../injector/container.js'; +import { DebugReplFn } from '../../../repl/native-functions/index.js'; +import { ReplContext } from '../../../repl/repl-context.js'; describe('DebugReplFn', () => { let debugReplFn: DebugReplFn; @@ -11,12 +9,12 @@ describe('DebugReplFn', () => { let replContext: ReplContext; let mockApp: { container: NestContainer; - get: sinon.SinonStub; - resolve: sinon.SinonSpy; - select: sinon.SinonSpy; + get: ReturnType; + resolve: ReturnType; + select: ReturnType; }; - before(async () => { + beforeAll(async () => { const container = new NestContainer(); const { moduleRef: aModuleRef } = (await container.addModule( class ModuleA {}, @@ -42,9 +40,9 @@ describe('DebugReplFn', () => { mockApp = { container, - get: sinon.stub(), - resolve: sinon.spy(), - select: sinon.spy(), + get: vi.fn(), + resolve: vi.fn(), + select: vi.fn(), }; replContext = new ReplContext(mockApp as any); }); @@ -53,27 +51,27 @@ describe('DebugReplFn', () => { debugReplFn = replContext.nativeFunctions.get('debug') as DebugReplFn; // To avoid coloring the output: - sinon.stub(clc, 'yellow').callsFake(text => text); - sinon.stub(clc, 'green').callsFake(text => text); + vi.spyOn(clc, 'yellow').mockImplementation(text => text); + vi.spyOn(clc, 'green').mockImplementation(text => text); }); - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); it('the function name should be "debug"', () => { - expect(debugReplFn).to.not.be.undefined; - expect(debugReplFn.fnDefinition.name).to.eql('debug'); + expect(debugReplFn).not.toBeUndefined(); + expect(debugReplFn.fnDefinition.name).toEqual('debug'); }); describe('action', () => { it('should print all modules along with their controllers and providers', () => { let outputText = ''; - sinon - .stub(replContext, 'writeToStdout') - .callsFake(text => (outputText += text)); + vi.spyOn(replContext, 'writeToStdout').mockImplementation( + text => (outputText += text), + ); debugReplFn.action(); - expect(outputText).to.equal(` + expect(outputText).toBe(` ModuleA: - controllers: ◻ ControllerA @@ -95,13 +93,13 @@ ModuleB: it("should print a specified module's controllers and providers", () => { let outputText = ''; - sinon - .stub(replContext, 'writeToStdout') - .callsFake(text => (outputText += text)); + vi.spyOn(replContext, 'writeToStdout').mockImplementation( + text => (outputText += text), + ); debugReplFn.action(class ModuleA {}); - expect(outputText).to.equal(` + expect(outputText).toBe(` ModuleA: - controllers: ◻ ControllerA @@ -118,13 +116,13 @@ ModuleA: it("should print a specified module's controllers and providers", () => { let outputText = ''; - sinon - .stub(replContext, 'writeToStdout') - .callsFake(text => (outputText += text)); + vi.spyOn(replContext, 'writeToStdout').mockImplementation( + text => (outputText += text), + ); debugReplFn.action('ModuleA'); - expect(outputText).to.equal(` + expect(outputText).toBe(` ModuleA: - controllers: ◻ ControllerA diff --git a/packages/core/test/repl/native-functions/get-repl-fn.spec.ts b/packages/core/test/repl/native-functions/get-repl-fn.spec.ts index aec8f335fac..c87cb444346 100644 --- a/packages/core/test/repl/native-functions/get-repl-fn.spec.ts +++ b/packages/core/test/repl/native-functions/get-repl-fn.spec.ts @@ -1,8 +1,6 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { GetReplFn } from '../../../repl/native-functions'; -import { ReplContext } from '../../../repl/repl-context'; -import { NestContainer } from '../../../injector/container'; +import { GetReplFn } from '../../../repl/native-functions/index.js'; +import { ReplContext } from '../../../repl/repl-context.js'; +import { NestContainer } from '../../../injector/container.js'; describe('GetReplFn', () => { let getReplFn: GetReplFn; @@ -10,19 +8,19 @@ describe('GetReplFn', () => { let replContext: ReplContext; let mockApp: { container: NestContainer; - get: sinon.SinonStub; - resolve: sinon.SinonSpy; - select: sinon.SinonSpy; + get: ReturnType; + resolve: ReturnType; + select: ReturnType; }; - before(async () => { + beforeAll(async () => { const container = new NestContainer(); mockApp = { container, - get: sinon.stub(), - resolve: sinon.spy(), - select: sinon.spy(), + get: vi.fn(), + resolve: vi.fn(), + select: vi.fn(), }; replContext = new ReplContext(mockApp as any); }); @@ -30,18 +28,18 @@ describe('GetReplFn', () => { beforeEach(() => { getReplFn = replContext.nativeFunctions.get('get') as GetReplFn; }); - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); it('the function name should be "get"', () => { - expect(getReplFn).to.not.be.undefined; - expect(getReplFn.fnDefinition.name).to.eql('get'); + expect(getReplFn).not.toBeUndefined(); + expect(getReplFn.fnDefinition.name).toEqual('get'); }); describe('action', () => { it('should pass arguments down to the application context', () => { const token = 'test'; getReplFn.action(token); - expect(mockApp.get.calledWith(token)).to.be.true; + expect(mockApp.get).toHaveBeenCalledWith(token); }); }); }); diff --git a/packages/core/test/repl/native-functions/help-repl-fn.spec.ts b/packages/core/test/repl/native-functions/help-repl-fn.spec.ts index 09f04115324..43e02e9659a 100644 --- a/packages/core/test/repl/native-functions/help-repl-fn.spec.ts +++ b/packages/core/test/repl/native-functions/help-repl-fn.spec.ts @@ -1,9 +1,7 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { HelpReplFn } from '../../../repl/native-functions'; -import { ReplContext } from '../../../repl/repl-context'; -import { NestContainer } from '../../../injector/container'; +import { clc } from '@nestjs/common/utils/cli-colors.util.js'; +import { NestContainer } from '../../../injector/container.js'; +import { HelpReplFn } from '../../../repl/native-functions/index.js'; +import { ReplContext } from '../../../repl/repl-context.js'; describe('HelpReplFn', () => { let helpReplFn: HelpReplFn; @@ -11,19 +9,19 @@ describe('HelpReplFn', () => { let replContext: ReplContext; let mockApp: { container: NestContainer; - get: sinon.SinonStub; - resolve: sinon.SinonSpy; - select: sinon.SinonSpy; + get: ReturnType; + resolve: ReturnType; + select: ReturnType; }; - before(async () => { + beforeAll(async () => { const container = new NestContainer(); mockApp = { container, - get: sinon.stub(), - resolve: sinon.spy(), - select: sinon.spy(), + get: vi.fn(), + resolve: vi.fn(), + select: vi.fn(), }; replContext = new ReplContext(mockApp as any); }); @@ -32,27 +30,27 @@ describe('HelpReplFn', () => { helpReplFn = replContext.nativeFunctions.get('help') as HelpReplFn; // To avoid coloring the output: - sinon.stub(clc, 'bold').callsFake(text => text); - sinon.stub(clc, 'cyanBright').callsFake(text => text); + vi.spyOn(clc, 'bold').mockImplementation(text => text); + vi.spyOn(clc, 'cyanBright').mockImplementation(text => text); }); - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); it('the function name should be "help"', () => { - expect(helpReplFn).to.not.be.undefined; - expect(helpReplFn.fnDefinition.name).to.eql('help'); + expect(helpReplFn).not.toBeUndefined(); + expect(helpReplFn.fnDefinition.name).toEqual('help'); }); describe('action', () => { it('should print all available native functions and their description', () => { let outputText = ''; - sinon - .stub(replContext, 'writeToStdout') - .callsFake(text => (outputText += text)); + vi.spyOn(replContext, 'writeToStdout').mockImplementation( + text => (outputText += text), + ); helpReplFn.action(); - expect(outputText).to - .equal(`You can call .help on any function listed below (e.g.: help.help): + expect(outputText) + .toEqual(`You can call .help on any function listed below (e.g.: help.help): $ - Retrieves an instance of either injectable or controller, otherwise, throws exception. debug - Print all registered modules as a list together with their controllers and providers. diff --git a/packages/core/test/repl/native-functions/methods-repl-fn.spec.ts b/packages/core/test/repl/native-functions/methods-repl-fn.spec.ts index 6cf7c60e319..c2db1323817 100644 --- a/packages/core/test/repl/native-functions/methods-repl-fn.spec.ts +++ b/packages/core/test/repl/native-functions/methods-repl-fn.spec.ts @@ -1,9 +1,7 @@ -import { clc } from '@nestjs/common/utils/cli-colors.util'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { NestContainer } from '../../../injector/container'; -import { MethodsReplFn } from '../../../repl/native-functions'; -import { ReplContext } from '../../../repl/repl-context'; +import { clc } from '@nestjs/common/utils/cli-colors.util.js'; +import { NestContainer } from '../../../injector/container.js'; +import { MethodsReplFn } from '../../../repl/native-functions/index.js'; +import { ReplContext } from '../../../repl/repl-context.js'; describe('MethodsReplFn', () => { let methodsReplFn: MethodsReplFn; @@ -11,12 +9,12 @@ describe('MethodsReplFn', () => { let replContext: ReplContext; let mockApp: { container: NestContainer; - get: sinon.SinonStub; - resolve: sinon.SinonSpy; - select: sinon.SinonSpy; + get: ReturnType; + resolve: ReturnType; + select: ReturnType; }; - before(async () => { + beforeAll(async () => { const container = new NestContainer(); const { moduleRef: aModuleRef } = (await container.addModule( class ModuleA {}, @@ -36,9 +34,9 @@ describe('MethodsReplFn', () => { mockApp = { container, - get: sinon.stub(), - resolve: sinon.spy(), - select: sinon.spy(), + get: vi.fn(), + resolve: vi.fn(), + select: vi.fn(), }; replContext = new ReplContext(mockApp as any); }); @@ -47,14 +45,14 @@ describe('MethodsReplFn', () => { methodsReplFn = replContext.nativeFunctions.get('methods') as MethodsReplFn; // To avoid coloring the output: - sinon.stub(clc, 'yellow').callsFake(text => text); - sinon.stub(clc, 'green').callsFake(text => text); + vi.spyOn(clc, 'yellow').mockImplementation(text => text); + vi.spyOn(clc, 'green').mockImplementation(text => text); }); - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); it('the function name should be "methods"', () => { - expect(methodsReplFn).to.not.be.undefined; - expect(methodsReplFn.fnDefinition.name).to.eql('methods'); + expect(methodsReplFn).not.toBeUndefined(); + expect(methodsReplFn.fnDefinition.name).toEqual('methods'); }); describe('action', () => { @@ -70,13 +68,13 @@ describe('MethodsReplFn', () => { let outputText = ''; - sinon - .stub(replContext, 'writeToStdout') - .callsFake(text => (outputText += text)); + vi.spyOn(replContext, 'writeToStdout').mockImplementation( + text => (outputText += text), + ); methodsReplFn.action(TestService); - expect(outputText).to.equal(` + expect(outputText).toBe(` Methods: ◻ findAll ◻ findOne @@ -94,15 +92,15 @@ Methods: } let outputText = ''; - sinon - .stub(replContext, 'writeToStdout') - .callsFake(text => (outputText += text)); + vi.spyOn(replContext, 'writeToStdout').mockImplementation( + text => (outputText += text), + ); - mockApp.get.callsFake(() => new ProviderA1()); + mockApp.get.mockImplementation(() => new ProviderA1()); methodsReplFn.action('ProviderA1'); - expect(outputText).to.equal(` + expect(outputText).toBe(` Methods: ◻ findAll ◻ findOne diff --git a/packages/core/test/repl/native-functions/resolve-repl-fn.spec.ts b/packages/core/test/repl/native-functions/resolve-repl-fn.spec.ts index cf78bbd40e3..80d3703c505 100644 --- a/packages/core/test/repl/native-functions/resolve-repl-fn.spec.ts +++ b/packages/core/test/repl/native-functions/resolve-repl-fn.spec.ts @@ -1,8 +1,6 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ResolveReplFn } from '../../../repl/native-functions'; -import { ReplContext } from '../../../repl/repl-context'; -import { NestContainer } from '../../../injector/container'; +import { ResolveReplFn } from '../../../repl/native-functions/index.js'; +import { ReplContext } from '../../../repl/repl-context.js'; +import { NestContainer } from '../../../injector/container.js'; describe('ResolveReplFn', () => { let resolveReplFn: ResolveReplFn; @@ -10,19 +8,19 @@ describe('ResolveReplFn', () => { let replContext: ReplContext; let mockApp: { container: NestContainer; - get: sinon.SinonStub; - resolve: sinon.SinonSpy; - select: sinon.SinonSpy; + get: ReturnType; + resolve: ReturnType; + select: ReturnType; }; - before(async () => { + beforeAll(async () => { const container = new NestContainer(); mockApp = { container, - get: sinon.stub(), - resolve: sinon.spy(), - select: sinon.spy(), + get: vi.fn(), + resolve: vi.fn(), + select: vi.fn(), }; replContext = new ReplContext(mockApp as any); }); @@ -30,11 +28,11 @@ describe('ResolveReplFn', () => { beforeEach(() => { resolveReplFn = replContext.nativeFunctions.get('resolve') as ResolveReplFn; }); - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); it('the function name should be "resolve"', () => { - expect(resolveReplFn).to.not.be.undefined; - expect(resolveReplFn.fnDefinition.name).to.eql('resolve'); + expect(resolveReplFn).not.toBeUndefined(); + expect(resolveReplFn.fnDefinition.name).toEqual('resolve'); }); describe('action', () => { @@ -43,7 +41,7 @@ describe('ResolveReplFn', () => { const contextId = {}; await resolveReplFn.action(token, contextId); - expect(mockApp.resolve.calledWith(token, contextId)).to.be.true; + expect(mockApp.resolve).toHaveBeenCalledWith(token, contextId); }); }); }); diff --git a/packages/core/test/repl/native-functions/select-repl-fn.spec.ts b/packages/core/test/repl/native-functions/select-repl-fn.spec.ts index 22a033e542c..6323e45533b 100644 --- a/packages/core/test/repl/native-functions/select-repl-fn.spec.ts +++ b/packages/core/test/repl/native-functions/select-repl-fn.spec.ts @@ -1,8 +1,6 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { SelectReplFn } from '../../../repl/native-functions'; -import { ReplContext } from '../../../repl/repl-context'; -import { NestContainer } from '../../../injector/container'; +import { SelectReplFn } from '../../../repl/native-functions/index.js'; +import { ReplContext } from '../../../repl/repl-context.js'; +import { NestContainer } from '../../../injector/container.js'; describe('SelectReplFn', () => { let selectReplFn: SelectReplFn; @@ -10,19 +8,19 @@ describe('SelectReplFn', () => { let replContext: ReplContext; let mockApp: { container: NestContainer; - get: sinon.SinonStub; - resolve: sinon.SinonSpy; - select: sinon.SinonSpy; + get: ReturnType; + resolve: ReturnType; + select: ReturnType; }; - before(async () => { + beforeAll(async () => { const container = new NestContainer(); mockApp = { container, - get: sinon.stub(), - resolve: sinon.spy(), - select: sinon.spy(), + get: vi.fn(), + resolve: vi.fn(), + select: vi.fn(), }; replContext = new ReplContext(mockApp as any); }); @@ -30,18 +28,18 @@ describe('SelectReplFn', () => { beforeEach(() => { selectReplFn = replContext.nativeFunctions.get('select') as SelectReplFn; }); - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); it('the function name should be "select"', () => { - expect(selectReplFn).to.not.be.undefined; - expect(selectReplFn.fnDefinition.name).to.eql('select'); + expect(selectReplFn).not.toBeUndefined(); + expect(selectReplFn.fnDefinition.name).toEqual('select'); }); describe('action', () => { it('should pass arguments down to the application context', () => { const moduleCls = class TestModule {}; selectReplFn.action(moduleCls); - expect(mockApp.select.calledWith(moduleCls)).to.be.true; + expect(mockApp.select).toHaveBeenCalledWith(moduleCls); }); }); }); diff --git a/packages/core/test/repl/repl-context.spec.ts b/packages/core/test/repl/repl-context.spec.ts index 9654dfce5e8..a1e52e56cea 100644 --- a/packages/core/test/repl/repl-context.spec.ts +++ b/packages/core/test/repl/repl-context.spec.ts @@ -1,38 +1,38 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { NestContainer } from '../../injector/container'; -import { ReplContext } from '../../repl/repl-context'; +import { NestContainer } from '../../injector/container.js'; +import { ReplContext } from '../../repl/repl-context.js'; describe('ReplContext', () => { let replContext: ReplContext; let mockApp: { container: NestContainer; - get: sinon.SinonStub; - resolve: sinon.SinonSpy; - select: sinon.SinonSpy; + get: ReturnType; + resolve: ReturnType; + select: ReturnType; }; - before(async () => { + beforeAll(async () => { const container = new NestContainer(); mockApp = { container, - get: sinon.stub(), - resolve: sinon.spy(), - select: sinon.spy(), + get: vi.fn(), + resolve: vi.fn(), + select: vi.fn(), }; replContext = new ReplContext(mockApp as any); }); - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); it('writeToStdout', () => { - const stdOutWrite = sinon.stub(process.stdout, 'write'); - const text = sinon.stub() as unknown as string; + const stdOutWrite = vi + .spyOn(process.stdout, 'write') + .mockImplementation(() => ({}) as any); + const text = vi.fn() as unknown as string; replContext.writeToStdout(text); - expect(stdOutWrite.calledOnce).to.be.true; - expect(stdOutWrite.calledWith(text)).to.be.true; + expect(stdOutWrite).toHaveBeenCalledOnce(); + expect(stdOutWrite).toHaveBeenCalledWith(text); }); }); diff --git a/packages/core/test/repl/repl-logger.spec.ts b/packages/core/test/repl/repl-logger.spec.ts new file mode 100644 index 00000000000..ce9134e6e93 --- /dev/null +++ b/packages/core/test/repl/repl-logger.spec.ts @@ -0,0 +1,46 @@ +import { ReplLogger } from '../../repl/repl-logger.js'; + +describe('ReplLogger', () => { + let logger: ReplLogger; + let superLogSpy: ReturnType; + + beforeEach(() => { + logger = new ReplLogger(); + // Spy on the parent class log method that ReplLogger conditionally delegates to + superLogSpy = vi + .spyOn(Object.getPrototypeOf(ReplLogger.prototype), 'log') + .mockImplementation(() => {}); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('log', () => { + it('should suppress logs with RoutesResolver context', () => { + logger.log('some message', 'RoutesResolver'); + expect(superLogSpy).not.toHaveBeenCalled(); + }); + + it('should suppress logs with RouterExplorer context', () => { + logger.log('some message', 'RouterExplorer'); + expect(superLogSpy).not.toHaveBeenCalled(); + }); + + it('should suppress logs with NestApplication context', () => { + logger.log('some message', 'NestApplication'); + expect(superLogSpy).not.toHaveBeenCalled(); + }); + + it('should pass through logs with other contexts', () => { + logger.log('some message', 'SomeOtherContext'); + expect(superLogSpy).toHaveBeenCalled(); + }); + + it('should pass through logs with no context', () => { + logger.log('some message'); + // context is undefined, includes(undefined!) returns false, so super.log is called + expect(superLogSpy).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/core/test/router/legacy-route-converter.spec.ts b/packages/core/test/router/legacy-route-converter.spec.ts new file mode 100644 index 00000000000..8a0b3b0f630 --- /dev/null +++ b/packages/core/test/router/legacy-route-converter.spec.ts @@ -0,0 +1,140 @@ +import { LegacyRouteConverter } from '../../router/legacy-route-converter.js'; + +describe('LegacyRouteConverter', () => { + let warnSpy: ReturnType; + let errorSpy: ReturnType; + + beforeEach(() => { + warnSpy = vi + .spyOn(LegacyRouteConverter['logger'], 'warn') + .mockImplementation(() => {}); + errorSpy = vi + .spyOn(LegacyRouteConverter['logger'], 'error') + .mockImplementation(() => {}); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('tryConvert', () => { + describe('(.*) wildcard', () => { + it('should convert trailing (.*) to {*path}', () => { + expect(LegacyRouteConverter.tryConvert('/users/(.*)')).toBe( + '/users/{*path}', + ); + }); + + it('should not print warning for root-level /(.*)', () => { + LegacyRouteConverter.tryConvert('/(.*)', { logs: true }); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should print warning for non-root (.*)', () => { + LegacyRouteConverter.tryConvert('/users/(.*)', { logs: true }); + expect(warnSpy).toHaveBeenCalled(); + }); + + it('should convert (.*) without leading slash', () => { + expect(LegacyRouteConverter.tryConvert('users/(.*)')).toBe( + 'users/{*path}', + ); + }); + }); + + describe('* wildcard', () => { + it('should convert trailing * to {*path}', () => { + expect(LegacyRouteConverter.tryConvert('/users/*')).toBe( + '/users/{*path}', + ); + }); + + it('should not print warning for root-level /*', () => { + LegacyRouteConverter.tryConvert('/*', { logs: true }); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should print warning for non-root *', () => { + LegacyRouteConverter.tryConvert('/users/*', { logs: true }); + expect(warnSpy).toHaveBeenCalled(); + }); + }); + + describe('+ wildcard', () => { + it('should convert /+ to /*path', () => { + expect(LegacyRouteConverter.tryConvert('/users/+')).toBe( + '/users/*path', + ); + }); + + it('should print warning for + wildcard', () => { + LegacyRouteConverter.tryConvert('/users/+', { logs: true }); + expect(warnSpy).toHaveBeenCalled(); + }); + }); + + describe('mid-path wildcards', () => { + it('should convert mid-path * segments to named params', () => { + const result = LegacyRouteConverter.tryConvert('/a/*/b'); + expect(result).toContain('/*path'); + expect(result).toContain('/b'); + }); + + it('should print warning for mid-path wildcards', () => { + LegacyRouteConverter.tryConvert('/a/*/b', { logs: true }); + expect(warnSpy).toHaveBeenCalled(); + }); + }); + + describe('no-op routes', () => { + it('should return route unchanged when no wildcards present', () => { + expect(LegacyRouteConverter.tryConvert('/users/:id')).toBe( + '/users/:id', + ); + }); + + it('should return empty route unchanged', () => { + expect(LegacyRouteConverter.tryConvert('/')).toBe('/'); + }); + + it('should return already-valid wildcard routes unchanged', () => { + expect(LegacyRouteConverter.tryConvert('/users/{*path}')).toBe( + '/users/{*path}', + ); + }); + }); + + describe('logs option', () => { + it('should suppress warnings when logs is false', () => { + LegacyRouteConverter.tryConvert('/users/*', { logs: false }); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should print warnings by default', () => { + LegacyRouteConverter.tryConvert('/users/*'); + expect(warnSpy).toHaveBeenCalled(); + }); + }); + }); + + describe('printError', () => { + it('should log an error message with the route', () => { + LegacyRouteConverter.printError('/users/*'); + expect(errorSpy).toHaveBeenCalledWith( + expect.stringContaining('Unsupported route path'), + ); + expect(errorSpy).toHaveBeenCalledWith( + expect.stringContaining('/users/*'), + ); + }); + }); + + describe('printWarning', () => { + it('should log a warning message with auto-convert note', () => { + LegacyRouteConverter.printWarning('/users/*'); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('Attempting to auto-convert'), + ); + }); + }); +}); diff --git a/packages/core/test/router/paths-explorer.spec.ts b/packages/core/test/router/paths-explorer.spec.ts index 053a566a667..59d0b40caa6 100644 --- a/packages/core/test/router/paths-explorer.spec.ts +++ b/packages/core/test/router/paths-explorer.spec.ts @@ -1,13 +1,12 @@ -import { expect } from 'chai'; -import { Controller } from '../../../common/decorators/core/controller.decorator'; +import { Controller } from '../../../common/decorators/core/controller.decorator.js'; import { All, Get, Post, -} from '../../../common/decorators/http/request-mapping.decorator'; -import { RequestMethod } from '../../../common/enums/request-method.enum'; -import { MetadataScanner } from '../../metadata-scanner'; -import { PathsExplorer } from '../../router/paths-explorer'; +} from '../../../common/decorators/http/request-mapping.decorator.js'; +import { RequestMethod } from '../../../common/enums/request-method.enum.js'; +import { MetadataScanner } from '../../metadata-scanner.js'; +import { PathsExplorer } from '../../router/paths-explorer.js'; describe('PathsExplorer', () => { @Controller('global') @@ -50,33 +49,33 @@ describe('PathsExplorer', () => { it('should method return expected list of route paths', () => { const paths = pathsExplorer.scanForPaths(new TestRoute()); - expect(paths).to.have.length(4); + expect(paths).toHaveLength(4); - expect(paths[0].path).to.eql(['/test']); - expect(paths[1].path).to.eql(['/test']); - expect(paths[2].path).to.eql(['/another-test']); - expect(paths[3].path).to.eql(['/foo', '/bar']); + expect(paths[0].path).toEqual(['/test']); + expect(paths[1].path).toEqual(['/test']); + expect(paths[2].path).toEqual(['/another-test']); + expect(paths[3].path).toEqual(['/foo', '/bar']); - expect(paths[0].requestMethod).to.eql(RequestMethod.GET); - expect(paths[1].requestMethod).to.eql(RequestMethod.POST); - expect(paths[2].requestMethod).to.eql(RequestMethod.ALL); - expect(paths[3].requestMethod).to.eql(RequestMethod.GET); + expect(paths[0].requestMethod).toEqual(RequestMethod.GET); + expect(paths[1].requestMethod).toEqual(RequestMethod.POST); + expect(paths[2].requestMethod).toEqual(RequestMethod.ALL); + expect(paths[3].requestMethod).toEqual(RequestMethod.GET); }); it('should method return expected list of route paths alias', () => { const paths = pathsExplorer.scanForPaths(new TestRouteAlias()); - expect(paths).to.have.length(4); + expect(paths).toHaveLength(4); - expect(paths[0].path).to.eql(['/test']); - expect(paths[1].path).to.eql(['/test']); - expect(paths[2].path).to.eql(['/another-test']); - expect(paths[3].path).to.eql(['/foo', '/bar']); + expect(paths[0].path).toEqual(['/test']); + expect(paths[1].path).toEqual(['/test']); + expect(paths[2].path).toEqual(['/another-test']); + expect(paths[3].path).toEqual(['/foo', '/bar']); - expect(paths[0].requestMethod).to.eql(RequestMethod.GET); - expect(paths[1].requestMethod).to.eql(RequestMethod.POST); - expect(paths[2].requestMethod).to.eql(RequestMethod.ALL); - expect(paths[3].requestMethod).to.eql(RequestMethod.GET); + expect(paths[0].requestMethod).toEqual(RequestMethod.GET); + expect(paths[1].requestMethod).toEqual(RequestMethod.POST); + expect(paths[2].requestMethod).toEqual(RequestMethod.ALL); + expect(paths[3].requestMethod).toEqual(RequestMethod.GET); }); }); @@ -91,9 +90,9 @@ describe('PathsExplorer', () => { 'getTest', )!; - expect(route.path).to.eql(['/test']); - expect(route.requestMethod).to.eql(RequestMethod.GET); - expect(route.targetCallback).to.eq(instance.getTest); + expect(route.path).toEqual(['/test']); + expect(route.requestMethod).toEqual(RequestMethod.GET); + expect(route.targetCallback).toBe(instance.getTest); }); it('should method return expected object which represent single route with alias', () => { @@ -106,9 +105,9 @@ describe('PathsExplorer', () => { 'getTest', )!; - expect(route.path).to.eql(['/test']); - expect(route.requestMethod).to.eql(RequestMethod.GET); - expect(route.targetCallback).to.eq(instance.getTest); + expect(route.path).toEqual(['/test']); + expect(route.requestMethod).toEqual(RequestMethod.GET); + expect(route.targetCallback).toBe(instance.getTest); }); it('should method return expected object which represent multiple routes', () => { @@ -121,9 +120,9 @@ describe('PathsExplorer', () => { 'getTestUsingArray', )!; - expect(route.path).to.eql(['/foo', '/bar']); - expect(route.requestMethod).to.eql(RequestMethod.GET); - expect(route.targetCallback).to.eq(instance.getTestUsingArray); + expect(route.path).toEqual(['/foo', '/bar']); + expect(route.requestMethod).toEqual(RequestMethod.GET); + expect(route.targetCallback).toBe(instance.getTestUsingArray); }); it('should method return expected object which represent multiple routes with alias', () => { @@ -136,9 +135,9 @@ describe('PathsExplorer', () => { 'getTestUsingArray', )!; - expect(route.path).to.eql(['/foo', '/bar']); - expect(route.requestMethod).to.eql(RequestMethod.GET); - expect(route.targetCallback).to.eq(instance.getTestUsingArray); + expect(route.path).toEqual(['/foo', '/bar']); + expect(route.requestMethod).toEqual(RequestMethod.GET); + expect(route.targetCallback).toBe(instance.getTestUsingArray); }); describe('when new implementation is injected into router', () => { @@ -155,9 +154,9 @@ describe('PathsExplorer', () => { 'getTest', )!; - expect(route.targetCallback).to.eq(newImpl); - expect(route.path).to.eql(['/test']); - expect(route.requestMethod).to.eql(RequestMethod.GET); + expect(route.targetCallback).toBe(newImpl); + expect(route.path).toEqual(['/test']); + expect(route.requestMethod).toEqual(RequestMethod.GET); }); it('should method return changed impl of single route which alias applied', () => { @@ -173,9 +172,9 @@ describe('PathsExplorer', () => { 'getTest', )!; - expect(route.targetCallback).to.eq(newImpl); - expect(route.path).to.eql(['/test']); - expect(route.requestMethod).to.eql(RequestMethod.GET); + expect(route.targetCallback).toBe(newImpl); + expect(route.path).toEqual(['/test']); + expect(route.requestMethod).toEqual(RequestMethod.GET); }); it('should method return changed impl of multiple routes', () => { @@ -191,9 +190,9 @@ describe('PathsExplorer', () => { 'getTestUsingArray', )!; - expect(route.targetCallback).to.eq(newImpl); - expect(route.path).to.eql(['/foo', '/bar']); - expect(route.requestMethod).to.eql(RequestMethod.GET); + expect(route.targetCallback).toBe(newImpl); + expect(route.path).toEqual(['/foo', '/bar']); + expect(route.requestMethod).toEqual(RequestMethod.GET); }); it('should method return changed impl of multiple routes which alias applied', () => { @@ -209,9 +208,9 @@ describe('PathsExplorer', () => { 'getTestUsingArray', )!; - expect(route.targetCallback).to.eq(newImpl); - expect(route.path).to.eql(['/foo', '/bar']); - expect(route.requestMethod).to.eql(RequestMethod.GET); + expect(route.targetCallback).toBe(newImpl); + expect(route.path).toEqual(['/foo', '/bar']); + expect(route.requestMethod).toEqual(RequestMethod.GET); }); }); }); diff --git a/packages/core/test/router/route-params-factory.spec.ts b/packages/core/test/router/route-params-factory.spec.ts index 3768dc9abb8..411d2dbc84c 100644 --- a/packages/core/test/router/route-params-factory.spec.ts +++ b/packages/core/test/router/route-params-factory.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum'; -import { RouteParamsFactory } from '../../router/route-params-factory'; +import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum.js'; +import { RouteParamsFactory } from '../../router/route-params-factory.js'; describe('RouteParamsFactory', () => { let factory: RouteParamsFactory; @@ -42,7 +41,7 @@ describe('RouteParamsFactory', () => { it('should return next object', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.NEXT, ...args), - ).to.be.eql(next); + ).toEqual(next); }); }); describe(`RouteParamtypes.RESPONSE`, () => { @@ -52,7 +51,7 @@ describe('RouteParamsFactory', () => { RouteParamtypes.RESPONSE, ...args, ), - ).to.be.eql(res); + ).toEqual(res); }); }); describe(`RouteParamtypes.REQUEST`, () => { @@ -62,14 +61,14 @@ describe('RouteParamsFactory', () => { RouteParamtypes.REQUEST, ...args, ), - ).to.be.eql(req); + ).toEqual(req); }); }); describe(`RouteParamtypes.BODY`, () => { it('should return body object', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.BODY, ...args), - ).to.be.eql(req.body); + ).toEqual(req.body); }); }); describe(`RouteParamtypes.RAW_BODY`, () => { @@ -79,7 +78,7 @@ describe('RouteParamsFactory', () => { RouteParamtypes.RAW_BODY, ...args, ), - ).to.be.eql(req.rawBody); + ).toEqual(req.rawBody); }); }); describe(`RouteParamtypes.HEADERS`, () => { @@ -89,14 +88,14 @@ describe('RouteParamsFactory', () => { RouteParamtypes.HEADERS, ...args, ), - ).to.be.eql(req.headers); + ).toEqual(req.headers); }); }); describe(`RouteParamtypes.IP`, () => { it('should return ip property', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.IP, ...args), - ).to.be.equal(req.ip); + ).toBe(req.ip); }); }); describe(`RouteParamtypes.SESSION`, () => { @@ -106,49 +105,47 @@ describe('RouteParamsFactory', () => { RouteParamtypes.SESSION, ...args, ), - ).to.be.eql(req.session); + ).toEqual(req.session); }); }); describe(`RouteParamtypes.QUERY`, () => { it('should return query object', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.QUERY, ...args), - ).to.be.eql(req.query); + ).toEqual(req.query); }); }); describe(`RouteParamtypes.PARAM`, () => { it('should return params object', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.PARAM, ...args), - ).to.be.eql(req.params); + ).toEqual(req.params); }); }); describe(`RouteParamtypes.HOST`, () => { it('should return hosts object', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.HOST, ...args), - ).to.be.eql(req.hosts); + ).toEqual(req.hosts); }); }); describe(`RouteParamtypes.FILE`, () => { it('should return file object', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.FILE, ...args), - ).to.be.eql(req.file); + ).toEqual(req.file); }); }); describe(`RouteParamtypes.FILES`, () => { it('should return files object', () => { expect( untypedFactory.exchangeKeyForValue(RouteParamtypes.FILES, ...args), - ).to.be.eql(req.files); + ).toEqual(req.files); }); }); describe('not available', () => { it('should return null', () => { - expect(untypedFactory.exchangeKeyForValue(-1, ...args)).to.be.eql( - null, - ); + expect(untypedFactory.exchangeKeyForValue(-1, ...args)).toEqual(null); }); }); }); diff --git a/packages/core/test/router/route-path-factory.spec.ts b/packages/core/test/router/route-path-factory.spec.ts index acb3ca1f712..0f7b29326eb 100644 --- a/packages/core/test/router/route-path-factory.spec.ts +++ b/packages/core/test/router/route-path-factory.spec.ts @@ -1,9 +1,7 @@ import { RequestMethod, VERSION_NEUTRAL, VersioningType } from '@nestjs/common'; -import { expect } from 'chai'; import { pathToRegexp } from 'path-to-regexp'; -import * as sinon from 'sinon'; -import { ApplicationConfig } from '../../application-config'; -import { RoutePathFactory } from '../../router/route-path-factory'; +import { ApplicationConfig } from '../../application-config.js'; +import { RoutePathFactory } from '../../router/route-path-factory.js'; describe('RoutePathFactory', () => { let routePathFactory: RoutePathFactory; @@ -21,28 +19,28 @@ describe('RoutePathFactory', () => { ctrlPath: 'ctrlPath/', methodPath: '', }), - ).to.deep.equal(['/ctrlPath']); + ).toEqual(['/ctrlPath']); expect( routePathFactory.create({ ctrlPath: '/ctrlPath', methodPath: '', }), - ).to.deep.equal(['/ctrlPath']); + ).toEqual(['/ctrlPath']); expect( routePathFactory.create({ ctrlPath: '/ctrlPath/', methodPath: '/methodPath', }), - ).to.deep.equal(['/ctrlPath/methodPath']); + ).toEqual(['/ctrlPath/methodPath']); expect( routePathFactory.create({ ctrlPath: 'ctrlPath/', methodPath: 'methodPath/', }), - ).to.deep.equal(['/ctrlPath/methodPath']); + ).toEqual(['/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -50,7 +48,7 @@ describe('RoutePathFactory', () => { methodPath: 'methodPath', modulePath: 'modulePath', }), - ).to.deep.equal(['/modulePath/ctrlPath/methodPath']); + ).toEqual(['/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -58,7 +56,7 @@ describe('RoutePathFactory', () => { methodPath: 'methodPath', modulePath: '/modulePath', }), - ).to.deep.equal(['/modulePath/ctrlPath/methodPath']); + ).toEqual(['/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -66,7 +64,7 @@ describe('RoutePathFactory', () => { methodPath: '/methodPath/', modulePath: '/modulePath/', }), - ).to.deep.equal(['/modulePath/ctrlPath/methodPath']); + ).toEqual(['/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -75,7 +73,7 @@ describe('RoutePathFactory', () => { modulePath: '/modulePath/', globalPrefix: 'api', }), - ).to.deep.equal(['/api/modulePath/ctrlPath/methodPath']); + ).toEqual(['/api/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -84,7 +82,7 @@ describe('RoutePathFactory', () => { modulePath: '/modulePath/', globalPrefix: '/api', }), - ).to.deep.equal(['/api/modulePath/ctrlPath/methodPath']); + ).toEqual(['/api/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -99,7 +97,7 @@ describe('RoutePathFactory', () => { methodVersion: '1.0.0', controllerVersion: '1.1.1', }), - ).to.deep.equal(['/api/modulePath/ctrlPath/methodPath']); + ).toEqual(['/api/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -113,7 +111,7 @@ describe('RoutePathFactory', () => { methodVersion: '1.0.0', controllerVersion: '1.1.1', }), - ).to.deep.equal(['/api/v1.0.0/modulePath/ctrlPath/methodPath']); + ).toEqual(['/api/v1.0.0/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -126,7 +124,7 @@ describe('RoutePathFactory', () => { methodVersion: '1.0.0', controllerVersion: '1.1.1', }), - ).to.deep.equal(['/v1.0.0/modulePath/ctrlPath/methodPath']); + ).toEqual(['/v1.0.0/modulePath/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -139,7 +137,7 @@ describe('RoutePathFactory', () => { methodVersion: '1.0.0', controllerVersion: '1.1.1', }), - ).to.deep.equal(['/api/v1.0.0/ctrlPath/methodPath']); + ).toEqual(['/api/v1.0.0/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -151,7 +149,7 @@ describe('RoutePathFactory', () => { }, controllerVersion: '1.1.1', }), - ).to.deep.equal(['/api/v1.1.1/ctrlPath/methodPath']); + ).toEqual(['/api/v1.1.1/ctrlPath/methodPath']); expect( routePathFactory.create({ @@ -163,7 +161,7 @@ describe('RoutePathFactory', () => { }, controllerVersion: ['1.1.1', '1.2.3'], }), - ).to.deep.equal([ + ).toEqual([ '/api/v1.1.1/ctrlPath/methodPath', '/api/v1.2.3/ctrlPath/methodPath', ]); @@ -178,7 +176,7 @@ describe('RoutePathFactory', () => { }, controllerVersion: ['1.1.1', '1.2.3'], }), - ).to.deep.equal(['/api/v1.1.1', '/api/v1.2.3']); + ).toEqual(['/api/v1.1.1', '/api/v1.2.3']); expect( routePathFactory.create({ @@ -191,7 +189,7 @@ describe('RoutePathFactory', () => { defaultVersion: VERSION_NEUTRAL, }, }), - ).to.deep.equal(['/']); + ).toEqual(['/']); expect( routePathFactory.create({ @@ -204,7 +202,7 @@ describe('RoutePathFactory', () => { defaultVersion: ['1', VERSION_NEUTRAL], }, }), - ).to.deep.equal(['/v1', '/']); + ).toEqual(['/v1', '/']); expect( routePathFactory.create({ @@ -212,9 +210,11 @@ describe('RoutePathFactory', () => { methodPath: '', globalPrefix: '', }), - ).to.deep.equal(['/']); + ).toEqual(['/']); - sinon.stub(routePathFactory, 'isExcludedFromGlobalPrefix').returns(true); + vi.spyOn(routePathFactory, 'isExcludedFromGlobalPrefix').mockReturnValue( + true, + ); expect( routePathFactory.create({ ctrlPath: '/ctrlPath/', @@ -222,15 +222,15 @@ describe('RoutePathFactory', () => { modulePath: '/', globalPrefix: '/api', }), - ).to.deep.equal(['/ctrlPath']); - sinon.restore(); + ).toEqual(['/ctrlPath']); + vi.restoreAllMocks(); }); }); describe('isExcludedFromGlobalPrefix', () => { describe('when there is no exclude configuration', () => { it('should return false', () => { - sinon.stub(applicationConfig, 'getGlobalPrefixOptions').returns({ + vi.spyOn(applicationConfig, 'getGlobalPrefixOptions').mockReturnValue({ exclude: undefined, }); expect( @@ -238,51 +238,58 @@ describe('RoutePathFactory', () => { '/cats', RequestMethod.GET, ), - ).to.be.false; + ).toBe(false); }); }); describe('otherwise', () => { describe('when route is not excluded', () => { it('should return false', () => { - sinon.stub(applicationConfig, 'getGlobalPrefixOptions').returns({ - exclude: [ - { - path: '/random', - pathRegex: pathToRegexp('/random').regexp, - requestMethod: RequestMethod.ALL, - }, - ], - }); + vi.spyOn(applicationConfig, 'getGlobalPrefixOptions').mockReturnValue( + { + exclude: [ + { + path: '/random', + pathRegex: pathToRegexp('/random').regexp, + requestMethod: RequestMethod.ALL, + }, + ], + }, + ); expect( routePathFactory.isExcludedFromGlobalPrefix( '/cats', RequestMethod.GET, ), - ).to.be.false; + ).toBe(false); }); }); describe('when route is excluded (by path)', () => { it('should return true', () => { - sinon.stub(applicationConfig, 'getGlobalPrefixOptions').returns({ - exclude: [ - { - path: '/cats', - pathRegex: pathToRegexp('/cats').regexp, - requestMethod: RequestMethod.ALL, - }, - ], - }); + vi.spyOn(applicationConfig, 'getGlobalPrefixOptions').mockReturnValue( + { + exclude: [ + { + path: '/cats', + pathRegex: pathToRegexp('/cats').regexp, + requestMethod: RequestMethod.ALL, + }, + ], + }, + ); expect( routePathFactory.isExcludedFromGlobalPrefix( '/cats', RequestMethod.GET, ), - ).to.be.true; + ).toBe(true); }); describe('when route is excluded (by method and path)', () => { it('should return true', () => { - sinon.stub(applicationConfig, 'getGlobalPrefixOptions').returns({ + vi.spyOn( + applicationConfig, + 'getGlobalPrefixOptions', + ).mockReturnValue({ exclude: [ { path: '/cats', @@ -296,13 +303,13 @@ describe('RoutePathFactory', () => { '/cats', RequestMethod.GET, ), - ).to.be.true; + ).toBe(true); expect( routePathFactory.isExcludedFromGlobalPrefix( '/cats', RequestMethod.POST, ), - ).to.be.false; + ).toBe(false); }); }); }); @@ -318,7 +325,7 @@ describe('RoutePathFactory', () => { type: VersioningType.URI, prefix: false, }), - ).to.equal(''); + ).toBe(''); }); }); describe('and prefix is undefined', () => { @@ -327,7 +334,7 @@ describe('RoutePathFactory', () => { routePathFactory.getVersionPrefix({ type: VersioningType.URI, }), - ).to.equal('v'); + ).toBe('v'); }); }); describe('and prefix is specified', () => { @@ -337,7 +344,7 @@ describe('RoutePathFactory', () => { type: VersioningType.URI, prefix: 'test', }), - ).to.equal('test'); + ).toBe('test'); }); }); }); @@ -348,7 +355,7 @@ describe('RoutePathFactory', () => { type: VersioningType.HEADER, header: 'X', }), - ).to.equal('v'); + ).toBe('v'); }); }); }); diff --git a/packages/core/test/router/router-exception-filters.spec.ts b/packages/core/test/router/router-exception-filters.spec.ts index f0e9afbd74b..0d2e5f1f5bf 100644 --- a/packages/core/test/router/router-exception-filters.spec.ts +++ b/packages/core/test/router/router-exception-filters.spec.ts @@ -1,12 +1,10 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Catch } from '../../../common/decorators/core/catch.decorator'; -import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator'; -import { ApplicationConfig } from '../../application-config'; -import { NestContainer } from '../../injector/container'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { RouterExceptionFilters } from '../../router/router-exception-filters'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +import { Catch } from '../../../common/decorators/core/catch.decorator.js'; +import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { NestContainer } from '../../injector/container.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { RouterExceptionFilters } from '../../router/router-exception-filters.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('RouterExceptionFilters', () => { let applicationConfig: ApplicationConfig; @@ -30,7 +28,7 @@ describe('RouterExceptionFilters', () => { describe('when filters metadata is empty', () => { class EmptyMetadata {} beforeEach(() => { - sinon.stub(exceptionFilter, 'createContext').returns([]); + vi.spyOn(exceptionFilter, 'createContext').mockReturnValue([]); }); it('should return plain ExceptionHandler object', () => { const filter = exceptionFilter.create( @@ -38,7 +36,7 @@ describe('RouterExceptionFilters', () => { () => ({}) as any, undefined, ); - expect((filter as any).filters).to.be.empty; + expect((filter as any).filters).toHaveLength(0); }); }); describe('when filters metadata is not empty', () => { @@ -51,7 +49,7 @@ describe('RouterExceptionFilters', () => { () => ({}) as any, undefined, ); - expect((filter as any).filters).to.not.be.empty; + expect((filter as any).filters).not.toHaveLength(0); }); }); }); @@ -59,7 +57,7 @@ describe('RouterExceptionFilters', () => { it('should return FILTER_CATCH_EXCEPTIONS metadata', () => { expect( exceptionFilter.reflectCatchExceptions(new ExceptionFilter()), - ).to.be.eql([CustomException]); + ).toEqual([CustomException]); }); }); describe('createConcreteContext', () => { @@ -68,18 +66,16 @@ describe('RouterExceptionFilters', () => { it('should return expected exception filters metadata', () => { const resolved = exceptionFilter.createConcreteContext(filters as any); - expect(resolved).to.have.length(1); - expect(resolved[0].exceptionMetatypes).to.be.deep.equal([ - CustomException, - ]); - expect(resolved[0].func).to.be.a('function'); + expect(resolved).toHaveLength(1); + expect(resolved[0].exceptionMetatypes).toEqual([CustomException]); + expect(resolved[0].func).toBeTypeOf('function'); }); }); describe('getGlobalMetadata', () => { describe('when contextId is static and inquirerId is nil', () => { it('should return global filters', () => { const expectedResult = applicationConfig.getGlobalFilters(); - expect(exceptionFilter.getGlobalMetadata()).to.be.equal(expectedResult); + expect(exceptionFilter.getGlobalMetadata()).toBe(expectedResult); }); }); describe('otherwise', () => { @@ -89,19 +85,20 @@ describe('RouterExceptionFilters', () => { const instance = 'request-scoped'; const scopedFilterWrappers = [instanceWrapper]; - sinon - .stub(applicationConfig, 'getGlobalFilters') - .callsFake(() => globalFilters); - sinon - .stub(applicationConfig, 'getGlobalRequestFilters') - .callsFake(() => scopedFilterWrappers); - sinon - .stub(instanceWrapper, 'getInstanceByContextId') - .callsFake(() => ({ instance }) as any); + vi.spyOn(applicationConfig, 'getGlobalFilters').mockImplementation( + () => globalFilters, + ); + vi.spyOn( + applicationConfig, + 'getGlobalRequestFilters', + ).mockImplementation(() => scopedFilterWrappers); + vi.spyOn(instanceWrapper, 'getInstanceByContextId').mockImplementation( + () => ({ instance }) as any, + ); - expect(exceptionFilter.getGlobalMetadata({ id: 3 })).to.contains( - instance, - ...globalFilters, + const result = exceptionFilter.getGlobalMetadata({ id: 3 }); + expect(result).toEqual( + expect.arrayContaining([instance, ...globalFilters]), ); }); }); diff --git a/packages/core/test/router/router-execution-context.spec.ts b/packages/core/test/router/router-execution-context.spec.ts index ee7499dd5d7..9e8cbfdd345 100644 --- a/packages/core/test/router/router-execution-context.spec.ts +++ b/packages/core/test/router/router-execution-context.spec.ts @@ -1,31 +1,33 @@ -import { ForbiddenException } from '@nestjs/common/exceptions/forbidden.exception'; -import { expect } from 'chai'; +import { ForbiddenException } from '@nestjs/common/exceptions/forbidden.exception.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; import { PassThrough } from 'stream'; -import { HttpException, HttpStatus, RouteParamMetadata } from '../../../common'; -import { CUSTOM_ROUTE_ARGS_METADATA } from '../../../common/constants'; -import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum'; -import { AbstractHttpAdapter } from '../../adapters'; -import { ApplicationConfig } from '../../application-config'; -import { FORBIDDEN_MESSAGE } from '../../guards/constants'; -import { GuardsConsumer } from '../../guards/guards-consumer'; -import { GuardsContextCreator } from '../../guards/guards-context-creator'; -import { HandlerResponseBasicFn } from '../../helpers/handler-metadata-storage'; -import { NestContainer } from '../../injector/container'; -import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer'; -import { InterceptorsContextCreator } from '../../interceptors/interceptors-context-creator'; -import { PipesConsumer } from '../../pipes/pipes-consumer'; -import { PipesContextCreator } from '../../pipes/pipes-context-creator'; -import { RouteParamsFactory } from '../../router/route-params-factory'; -import { RouterExecutionContext } from '../../router/router-execution-context'; -import { HeaderStream } from '../../router/sse-stream'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +import { CUSTOM_ROUTE_ARGS_METADATA } from '../../../common/constants.js'; +import { RouteParamtypes } from '../../../common/enums/route-paramtypes.enum.js'; +import { + HttpException, + HttpStatus, + RouteParamMetadata, +} from '../../../common/index.js'; +import { AbstractHttpAdapter } from '../../adapters/index.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { FORBIDDEN_MESSAGE } from '../../guards/constants.js'; +import { GuardsConsumer } from '../../guards/guards-consumer.js'; +import { GuardsContextCreator } from '../../guards/guards-context-creator.js'; +import { HandlerResponseBasicFn } from '../../helpers/handler-metadata-storage.js'; +import { NestContainer } from '../../injector/container.js'; +import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer.js'; +import { InterceptorsContextCreator } from '../../interceptors/interceptors-context-creator.js'; +import { PipesConsumer } from '../../pipes/pipes-consumer.js'; +import { PipesContextCreator } from '../../pipes/pipes-context-creator.js'; +import { RouteParamsFactory } from '../../router/route-params-factory.js'; +import { RouterExecutionContext } from '../../router/router-execution-context.js'; +import { HeaderStream } from '../../router/sse-stream.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('RouterExecutionContext', () => { let contextCreator: RouterExecutionContext; let callback: any; - let applySpy: sinon.SinonSpy; + let applySpy: ReturnType; let factory: RouteParamsFactory; let consumer: PipesConsumer; let guardsConsumer: GuardsConsumer; @@ -37,7 +39,7 @@ describe('RouterExecutionContext', () => { bind: () => ({}), apply: () => ({}), }; - applySpy = sinon.spy(callback, 'apply'); + applySpy = vi.spyOn(callback, 'apply'); factory = new RouteParamsFactory(); consumer = new PipesConsumer(); @@ -58,7 +60,7 @@ describe('RouterExecutionContext', () => { describe('create', () => { describe('when callback metadata is not undefined', () => { let metadata: Record; - let exchangeKeysForValuesSpy: sinon.SinonSpy; + let exchangeKeysForValuesSpy: ReturnType; beforeEach(() => { metadata = { [RouteParamtypes.NEXT]: { index: 0 }, @@ -67,28 +69,35 @@ describe('RouterExecutionContext', () => { data: 'test', }, }; - sinon - .stub((contextCreator as any).contextUtils, 'reflectCallbackMetadata') - .returns(metadata); - sinon - .stub( - (contextCreator as any).contextUtils, - 'reflectCallbackParamtypes', - ) - .returns([]); - exchangeKeysForValuesSpy = sinon.spy( + vi.spyOn( + (contextCreator as any).contextUtils, + 'reflectCallbackMetadata', + ).mockReturnValue(metadata); + vi.spyOn( + (contextCreator as any).contextUtils, + 'reflectCallbackParamtypes', + ).mockReturnValue([]); + exchangeKeysForValuesSpy = vi.spyOn( contextCreator, 'exchangeKeysForValues', ); }); - it('should call "exchangeKeysForValues" with expected arguments', done => { - const keys = Object.keys(metadata); - - contextCreator.create({ foo: 'bar' }, callback, '', '', 0); - expect(exchangeKeysForValuesSpy.called).to.be.true; - expect(exchangeKeysForValuesSpy.calledWith(keys, metadata)).to.be.true; - done(); - }); + it('should call "exchangeKeysForValues" with expected arguments', () => + new Promise(done => { + const keys = Object.keys(metadata); + + contextCreator.create({ foo: 'bar' }, callback, '', '', 0); + expect(exchangeKeysForValuesSpy).toHaveBeenCalled(); + expect(exchangeKeysForValuesSpy).toHaveBeenCalledWith( + keys, + metadata, + '', + expect.anything(), + undefined, + expect.any(Function), + ); + done(); + })); describe('returns proxy function', () => { let proxyContext; let instance; @@ -101,14 +110,16 @@ describe('RouterExecutionContext', () => { null!, null!, ); - sinon.stub(contextCreator, 'createGuardsFn').returns(canActivateFn); - tryActivateStub = sinon - .stub(guardsConsumer, 'tryActivate') - .callsFake(async () => true); + vi.spyOn(contextCreator, 'createGuardsFn').mockReturnValue( + canActivateFn, + ); + tryActivateStub = vi + .spyOn(guardsConsumer, 'tryActivate') + .mockImplementation(async () => true); proxyContext = contextCreator.create(instance, callback, '', '', 0); }); it('should be a function', () => { - expect(proxyContext).to.be.a('function'); + expect(proxyContext).toBeTypeOf('function'); }); describe('when proxy function called', () => { let request; @@ -126,17 +137,18 @@ describe('RouterExecutionContext', () => { }, }; }); - it('should apply expected context and arguments to callback', done => { - tryActivateStub.callsFake(async () => true); - proxyContext(request, response, next).then(() => { - const args = [next, undefined, request.body.test]; - expect(applySpy.called).to.be.true; - expect(applySpy.calledWith(instance, args)).to.be.true; - done(); - }); - }); + it('should apply expected context and arguments to callback', () => + new Promise(done => { + tryActivateStub.mockImplementation(async () => true); + proxyContext(request, response, next).then(() => { + const args = [next, undefined, request.body.test]; + expect(applySpy).toHaveBeenCalled(); + expect(applySpy).toHaveBeenCalledWith(instance, args); + done(); + }); + })); it('should throw exception when "tryActivate" returns false', async () => { - tryActivateStub.callsFake(async () => false); + tryActivateStub.mockImplementation(async () => false); let error: HttpException; try { @@ -144,9 +156,9 @@ describe('RouterExecutionContext', () => { } catch (e) { error = e; } - expect(error!).to.be.instanceOf(ForbiddenException); - expect(error!.message).to.be.eql('Forbidden resource'); - expect(error!.getResponse()).to.be.eql({ + expect(error!).toBeInstanceOf(ForbiddenException); + expect(error!.message).toEqual('Forbidden resource'); + expect(error!.getResponse()).toEqual({ statusCode: HttpStatus.FORBIDDEN, error: 'Forbidden', message: FORBIDDEN_MESSAGE, @@ -154,17 +166,19 @@ describe('RouterExecutionContext', () => { }); it('should apply expected context when "canActivateFn" apply', () => { proxyContext(request, response, next).then(() => { - expect(tryActivateStub.args[0][1][0]).to.equals(request); - expect(tryActivateStub.args[0][1][1]).to.equals(response); - expect(tryActivateStub.args[0][1][2]).to.equals(next); + expect(tryActivateStub.mock.calls[0][1][0]).toBe(request); + expect(tryActivateStub.mock.calls[0][1][1]).toBe(response); + expect(tryActivateStub.mock.calls[0][1][2]).toBe(next); }); }); it('should apply expected context when "intercept" apply', () => { - const interceptStub = sinon.stub(interceptorsConsumer, 'intercept'); + const interceptStub = vi + .spyOn(interceptorsConsumer, 'intercept') + .mockImplementation(() => ({}) as any); proxyContext(request, response, next).then(() => { - expect(interceptStub.args[0][1][0]).to.equals(request); - expect(interceptStub.args[0][1][1]).to.equals(response); - expect(interceptStub.args[0][1][2]).to.equals(next); + expect(interceptStub.mock.calls[0][1][0]).toBe(request); + expect(interceptStub.mock.calls[0][1][1]).toBe(response); + expect(interceptStub.mock.calls[0][1][2]).toBe(next); }); }); }); @@ -190,19 +204,19 @@ describe('RouterExecutionContext', () => { { index: 2, type: RouteParamtypes.BODY, data: 'test' }, { index: 3, type: `key${CUSTOM_ROUTE_ARGS_METADATA}`, data: 'custom' }, ]; - expect(values[0]).to.deep.include(expectedValues[0]); - expect(values[1]).to.deep.include(expectedValues[1]); + expect(values[0]).toMatchObject(expectedValues[0]); + expect(values[1]).toMatchObject(expectedValues[1]); }); }); describe('getParamValue', () => { - let consumerApplySpy: sinon.SinonSpy; + let consumerApplySpy: ReturnType; const value = 3, metatype = null, - transforms = [{ transform: sinon.spy() }]; + transforms = [{ transform: vi.fn() }]; beforeEach(() => { - consumerApplySpy = sinon.spy(consumer, 'apply'); + consumerApplySpy = vi.spyOn(consumer, 'apply'); }); describe('when paramtype is query, body, rawBody or param', () => { it('should call "consumer.apply" with expected arguments', async () => { @@ -211,52 +225,44 @@ describe('RouterExecutionContext', () => { { metatype, type: RouteParamtypes.QUERY, data: null }, transforms, ); - expect( - consumerApplySpy.calledWith( - value, - { metatype, type: RouteParamtypes.QUERY, data: null }, - transforms, - ), - ).to.be.true; + expect(consumerApplySpy).toHaveBeenCalledWith( + value, + { metatype, type: RouteParamtypes.QUERY, data: null }, + transforms, + ); await contextCreator.getParamValue( value, { metatype, type: RouteParamtypes.BODY, data: null }, transforms, ); - expect( - consumerApplySpy.calledWith( - value, - { metatype, type: RouteParamtypes.BODY, data: null }, - transforms, - ), - ).to.be.true; + expect(consumerApplySpy).toHaveBeenCalledWith( + value, + { metatype, type: RouteParamtypes.BODY, data: null }, + transforms, + ); await contextCreator.getParamValue( value, { metatype, type: RouteParamtypes.RAW_BODY, data: null }, transforms, ); - expect( - consumerApplySpy.calledWith( - value, - { metatype, type: RouteParamtypes.RAW_BODY, data: null }, - transforms, - ), - ).to.be.true; + expect(consumerApplySpy).toHaveBeenCalledWith( + value, + { metatype, type: RouteParamtypes.RAW_BODY, data: null }, + transforms, + ); await contextCreator.getParamValue( value, { metatype, type: RouteParamtypes.PARAM, data: null }, transforms, ); - expect( - consumerApplySpy.calledWith( - value, - { metatype, type: RouteParamtypes.PARAM, data: null }, - transforms, - ), - ).to.be.true; + expect(consumerApplySpy).toHaveBeenCalledWith( + value, + { metatype, type: RouteParamtypes.PARAM, data: null }, + transforms, + ); }); }); }); @@ -264,16 +270,16 @@ describe('RouterExecutionContext', () => { describe('when paramtype is not query, body, param and custom', () => { it('should return false', () => { const result = contextCreator.isPipeable(RouteParamtypes.NEXT); - expect(result).to.be.false; + expect(result).toBe(false); }); it('otherwise', () => { - expect(contextCreator.isPipeable(RouteParamtypes.BODY)).to.be.true; - expect(contextCreator.isPipeable(RouteParamtypes.RAW_BODY)).to.be.true; - expect(contextCreator.isPipeable(RouteParamtypes.QUERY)).to.be.true; - expect(contextCreator.isPipeable(RouteParamtypes.PARAM)).to.be.true; - expect(contextCreator.isPipeable(RouteParamtypes.FILE)).to.be.true; - expect(contextCreator.isPipeable(RouteParamtypes.FILES)).to.be.true; - expect(contextCreator.isPipeable('custom')).to.be.true; + expect(contextCreator.isPipeable(RouteParamtypes.BODY)).toBe(true); + expect(contextCreator.isPipeable(RouteParamtypes.RAW_BODY)).toBe(true); + expect(contextCreator.isPipeable(RouteParamtypes.QUERY)).toBe(true); + expect(contextCreator.isPipeable(RouteParamtypes.PARAM)).toBe(true); + expect(contextCreator.isPipeable(RouteParamtypes.FILE)).toBe(true); + expect(contextCreator.isPipeable(RouteParamtypes.FILES)).toBe(true); + expect(contextCreator.isPipeable('custom')).toBe(true); }); }); }); @@ -281,14 +287,16 @@ describe('RouterExecutionContext', () => { describe('when "paramsOptions" is empty', () => { it('returns null', async () => { const pipesFn = contextCreator.createPipesFn([], []); - expect(pipesFn).to.be.null; + expect(pipesFn).toBeNull(); }); }); }); describe('createGuardsFn', () => { it('should throw ForbiddenException when "tryActivate" returns false', async () => { const guardsFn = contextCreator.createGuardsFn([null!], null!, null!)!; - sinon.stub(guardsConsumer, 'tryActivate').callsFake(async () => false); + vi.spyOn(guardsConsumer, 'tryActivate').mockImplementation( + async () => false, + ); let error: ForbiddenException; try { @@ -297,9 +305,9 @@ describe('RouterExecutionContext', () => { error = e; } - expect(error!).to.be.instanceOf(ForbiddenException); - expect(error!.message).to.be.eql('Forbidden resource'); - expect(error!.getResponse()).to.be.eql({ + expect(error!).toBeInstanceOf(ForbiddenException); + expect(error!.message).toEqual('Forbidden resource'); + expect(error!.getResponse()).toEqual({ statusCode: HttpStatus.FORBIDDEN, message: FORBIDDEN_MESSAGE, error: 'Forbidden', @@ -309,18 +317,20 @@ describe('RouterExecutionContext', () => { describe('createHandleResponseFn', () => { describe('when "renderTemplate" is defined', () => { beforeEach(() => { - sinon - .stub(adapter, 'render') - .callsFake((response, view: string, options: any) => { + vi.spyOn(adapter, 'render').mockImplementation( + (response, view: string, options: any) => { return response.render(view, options); - }); + }, + ); }); it('should call "res.render()" with expected args', async () => { const template = 'template'; const value = 'test'; - const response = { render: sinon.spy() }; + const response = { render: vi.fn() }; - sinon.stub(contextCreator, 'reflectRenderTemplate').returns(template); + vi.spyOn(contextCreator, 'reflectRenderTemplate').mockReturnValue( + template, + ); const handler = contextCreator.createHandleResponseFn( null!, @@ -330,17 +340,19 @@ describe('RouterExecutionContext', () => { ) as HandlerResponseBasicFn; await handler(value, response); - expect(response.render.calledWith(template, value)).to.be.true; + expect(response.render).toHaveBeenCalledWith(template, value); }); }); describe('when "renderTemplate" is undefined', () => { it('should not call "res.render()"', async () => { const result = Promise.resolve('test'); - const response = { render: sinon.spy() }; + const response = { render: vi.fn() }; - sinon.stub(contextCreator, 'reflectResponseHeaders').returns([]); - sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined!); - sinon.stub(contextCreator, 'reflectSse').returns(undefined!); + vi.spyOn(contextCreator, 'reflectResponseHeaders').mockReturnValue([]); + vi.spyOn(contextCreator, 'reflectRenderTemplate').mockReturnValue( + undefined!, + ); + vi.spyOn(contextCreator, 'reflectSse').mockReturnValue(undefined!); const handler = contextCreator.createHandleResponseFn( null!, @@ -350,23 +362,23 @@ describe('RouterExecutionContext', () => { ) as HandlerResponseBasicFn; await handler(result, response); - expect(response.render.called).to.be.false; + expect(response.render).not.toHaveBeenCalled(); }); }); describe('when "redirectResponse" is present', () => { beforeEach(() => { - sinon - .stub(adapter, 'redirect') - .callsFake((response, statusCode: number, url: string) => { + vi.spyOn(adapter, 'redirect').mockImplementation( + (response, statusCode: number, url: string) => { return response.redirect(statusCode, url); - }); + }, + ); }); it('should call "res.redirect()" with expected args', async () => { const redirectResponse = { url: 'http://test.com', statusCode: 302, }; - const response = { redirect: sinon.spy() }; + const response = { redirect: vi.fn() }; const handler = contextCreator.createHandleResponseFn( () => {}, @@ -376,23 +388,23 @@ describe('RouterExecutionContext', () => { ) as HandlerResponseBasicFn; await handler(redirectResponse, response); - expect( - response.redirect.calledWith( - redirectResponse.statusCode, - redirectResponse.url, - ), - ).to.be.true; + expect(response.redirect).toHaveBeenCalledWith( + redirectResponse.statusCode, + redirectResponse.url, + ); }); }); describe('when "redirectResponse" is undefined', () => { it('should not call "res.redirect()"', async () => { const result = Promise.resolve('test'); - const response = { redirect: sinon.spy() }; + const response = { redirect: vi.fn() }; - sinon.stub(contextCreator, 'reflectResponseHeaders').returns([]); - sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined!); - sinon.stub(contextCreator, 'reflectSse').returns(undefined!); + vi.spyOn(contextCreator, 'reflectResponseHeaders').mockReturnValue([]); + vi.spyOn(contextCreator, 'reflectRenderTemplate').mockReturnValue( + undefined!, + ); + vi.spyOn(contextCreator, 'reflectSse').mockReturnValue(undefined!); const handler = contextCreator.createHandleResponseFn( null!, @@ -402,7 +414,7 @@ describe('RouterExecutionContext', () => { ) as HandlerResponseBasicFn; await handler(result, response); - expect(response.redirect.called).to.be.false; + expect(response.redirect).not.toHaveBeenCalled(); }); }); @@ -411,8 +423,10 @@ describe('RouterExecutionContext', () => { const result = Promise.resolve('test'); const response = {}; - sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined!); - sinon.stub(contextCreator, 'reflectSse').returns(undefined!); + vi.spyOn(contextCreator, 'reflectRenderTemplate').mockReturnValue( + undefined!, + ); + vi.spyOn(contextCreator, 'reflectSse').mockReturnValue(undefined!); const handler = contextCreator.createHandleResponseFn( null!, @@ -420,15 +434,9 @@ describe('RouterExecutionContext', () => { undefined, 1234, ) as HandlerResponseBasicFn; - const adapterReplySpy = sinon.spy(adapter, 'reply'); + const adapterReplySpy = vi.spyOn(adapter, 'reply'); await handler(result, response); - expect( - adapterReplySpy.calledOnceWithExactly( - sinon.match.same(response), - 'test', - 1234, - ), - ).to.be.true; + expect(adapterReplySpy).toHaveBeenCalledWith(response, 'test', 1234); }); }); @@ -436,13 +444,15 @@ describe('RouterExecutionContext', () => { it('should delegate result to SseStream', async () => { const result = of('test'); const response = new PassThrough(); - response.write = sinon.spy(); + response.write = vi.fn(); const request = new PassThrough(); - request.on = sinon.spy(); + request.on = vi.fn(); - sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined!); - sinon.stub(contextCreator, 'reflectSse').returns('/'); + vi.spyOn(contextCreator, 'reflectRenderTemplate').mockReturnValue( + undefined!, + ); + vi.spyOn(contextCreator, 'reflectSse').mockReturnValue('/'); const handler = contextCreator.createHandleResponseFn( null!, @@ -452,8 +462,8 @@ describe('RouterExecutionContext', () => { ) as HandlerResponseBasicFn; await handler(result, response, request); - expect((response.write as any).called).to.be.true; - expect((request.on as any).called).to.be.true; + expect(response.write).toHaveBeenCalled(); + expect(request.on).toHaveBeenCalled(); }); it('should not allow a non-observable result', async () => { @@ -461,8 +471,10 @@ describe('RouterExecutionContext', () => { const response = new PassThrough(); const request = new PassThrough(); - sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined!); - sinon.stub(contextCreator, 'reflectSse').returns('/'); + vi.spyOn(contextCreator, 'reflectRenderTemplate').mockReturnValue( + undefined!, + ); + vi.spyOn(contextCreator, 'reflectSse').mockReturnValue('/'); const handler = contextCreator.createHandleResponseFn( null!, @@ -474,7 +486,7 @@ describe('RouterExecutionContext', () => { try { await handler(result, response, request); } catch (e) { - expect(e.message).to.equal( + expect(e.message).toBe( 'You must return an Observable stream to use Server-Sent Events (SSE).', ); } @@ -483,18 +495,20 @@ describe('RouterExecutionContext', () => { it('should apply any headers that exists on the response', async () => { const result = of('test'); const response = new PassThrough() as HeaderStream; - response.write = sinon.spy(); - response.writeHead = sinon.spy(); - response.flushHeaders = sinon.spy(); - response.getHeaders = sinon - .stub() - .returns({ 'access-control-headers': 'some-cors-value' }); + response.write = vi.fn(); + response.writeHead = vi.fn(); + response.flushHeaders = vi.fn(); + response.getHeaders = vi + .fn() + .mockReturnValue({ 'access-control-headers': 'some-cors-value' }); const request = new PassThrough(); - request.on = sinon.spy(); + request.on = vi.fn(); - sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined!); - sinon.stub(contextCreator, 'reflectSse').returns('/'); + vi.spyOn(contextCreator, 'reflectRenderTemplate').mockReturnValue( + undefined!, + ); + vi.spyOn(contextCreator, 'reflectSse').mockReturnValue('/'); const handler = contextCreator.createHandleResponseFn( null!, @@ -504,12 +518,12 @@ describe('RouterExecutionContext', () => { ) as HandlerResponseBasicFn; await handler(result, response, request); - expect( - (response.writeHead as sinon.SinonSpy).calledWith( - 200, - sinon.match.hasNested('access-control-headers', 'some-cors-value'), - ), - ).to.be.true; + expect(response.writeHead).toHaveBeenCalledWith( + 200, + expect.objectContaining({ + 'access-control-headers': 'some-cors-value', + }), + ); }); }); }); diff --git a/packages/core/test/router/router-explorer.spec.ts b/packages/core/test/router/router-explorer.spec.ts index a5d32ad0a5e..c3982c66c26 100644 --- a/packages/core/test/router/router-explorer.spec.ts +++ b/packages/core/test/router/router-explorer.spec.ts @@ -1,26 +1,24 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Controller } from '../../../common/decorators/core/controller.decorator'; +import { Controller } from '../../../common/decorators/core/controller.decorator.js'; import { All, Get, Post, -} from '../../../common/decorators/http/request-mapping.decorator'; -import { RequestMethod } from '../../../common/enums/request-method.enum'; -import { VersioningType } from '../../../common/enums/version-type.enum'; -import { Injector } from '../../../core/injector/injector'; -import { ApplicationConfig } from '../../application-config'; -import { UnknownRequestMappingException } from '../../errors/exceptions/unknown-request-mapping.exception'; -import { ExecutionContextHost } from '../../helpers/execution-context-host'; -import { NestContainer } from '../../injector/container'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { GraphInspector } from '../../inspector/graph-inspector'; -import { MetadataScanner } from '../../metadata-scanner'; -import { RoutePathMetadata } from '../../router/interfaces/route-path-metadata.interface'; -import { RoutePathFactory } from '../../router/route-path-factory'; -import { RouterExceptionFilters } from '../../router/router-exception-filters'; -import { RouterExplorer } from '../../router/router-explorer'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +} from '../../../common/decorators/http/request-mapping.decorator.js'; +import { RequestMethod } from '../../../common/enums/request-method.enum.js'; +import { VersioningType } from '../../../common/enums/version-type.enum.js'; +import { Injector } from '../../../core/injector/injector.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { UnknownRequestMappingException } from '../../errors/exceptions/unknown-request-mapping.exception.js'; +import { ExecutionContextHost } from '../../helpers/execution-context-host.js'; +import { NestContainer } from '../../injector/container.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { GraphInspector } from '../../inspector/graph-inspector.js'; +import { MetadataScanner } from '../../metadata-scanner.js'; +import { RoutePathMetadata } from '../../router/interfaces/route-path-metadata.interface.js'; +import { RoutePathFactory } from '../../router/route-path-factory.js'; +import { RouterExceptionFilters } from '../../router/router-exception-filters.js'; +import { RouterExplorer } from '../../router/router-explorer.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('RouterExplorer', () => { @Controller('global') @@ -88,34 +86,40 @@ describe('RouterExplorer', () => { describe('applyPathsToRouterProxy', () => { it('should method return expected object which represent single route', () => { - const bindStub = sinon.stub( - routerBuilder, - 'applyCallbackToRouter' as any, - ); + const bindStub = vi + .spyOn(routerBuilder, 'applyCallbackToRouter' as any) + .mockImplementation(() => {}); const paths = [ { path: [''], requestMethod: RequestMethod.GET }, { path: ['test'], requestMethod: RequestMethod.GET }, { path: ['foo', 'bar'], requestMethod: RequestMethod.GET }, ]; + const mockInstanceWrapper = { instance: {} } as any; routerBuilder.applyPathsToRouterProxy( null!, paths as any, - null!, + mockInstanceWrapper, '', {}, '', ); - expect(bindStub.calledWith(null, paths[0], null)).to.be.true; - expect(bindStub.callCount).to.be.eql(paths.length); + expect(bindStub).toHaveBeenCalledWith( + null, + paths[0], + mockInstanceWrapper, + '', + { methodVersion: undefined }, + '', + ); + expect(bindStub).toHaveBeenCalledTimes(paths.length); }); it('should method return expected object which represents a single versioned route', () => { - const bindStub = sinon.stub( - routerBuilder, - 'applyCallbackToRouter' as any, - ); + const bindStub = vi + .spyOn(routerBuilder, 'applyCallbackToRouter' as any) + .mockImplementation(() => {}); const paths = [ { path: [''], requestMethod: RequestMethod.GET }, { path: ['test'], requestMethod: RequestMethod.GET }, @@ -125,29 +129,35 @@ describe('RouterExplorer', () => { const routePathMetadata: RoutePathMetadata = { versioningOptions: { type: VersioningType.URI }, }; + const mockInstanceWrapper = { instance: {} } as any; routerBuilder.applyPathsToRouterProxy( null!, paths as any, - null!, + mockInstanceWrapper, '', routePathMetadata, '1', ); - expect( - bindStub.calledWith(null, paths[0], null, '', routePathMetadata, '1'), - ).to.be.true; - expect(bindStub.callCount).to.be.eql(paths.length); + expect(bindStub).toHaveBeenCalledWith( + null, + paths[0], + mockInstanceWrapper, + '', + routePathMetadata, + '1', + ); + expect(bindStub).toHaveBeenCalledTimes(paths.length); }); }); describe('extractRouterPath', () => { it('should return expected path', () => { - expect(routerBuilder.extractRouterPath(TestRoute)).to.be.eql(['/global']); + expect(routerBuilder.extractRouterPath(TestRoute)).toEqual(['/global']); }); it('should return expected path with alias', () => { - expect(routerBuilder.extractRouterPath(TestRouteAlias)).to.be.eql([ + expect(routerBuilder.extractRouterPath(TestRouteAlias)).toEqual([ '/global', '/global-alias', ]); @@ -156,7 +166,7 @@ describe('RouterExplorer', () => { it("should throw UnknownRequestMappingException when missing the `@Controller()` decorator in the class, displaying class's name", () => { expect(() => routerBuilder.extractRouterPath(ClassWithMissingControllerDecorator), - ).to.throw( + ).toThrow( UnknownRequestMappingException, /ClassWithMissingControllerDecorator/, ); @@ -164,14 +174,14 @@ describe('RouterExplorer', () => { }); describe('createRequestScopedHandler', () => { - let nextSpy: sinon.SinonSpy; + let nextSpy: ReturnType; beforeEach(() => { - sinon.stub(injector, 'loadPerContext').callsFake(() => { + vi.spyOn(injector, 'loadPerContext').mockImplementation(() => { throw new Error(); }); - nextSpy = sinon.spy(); - sinon.stub(exceptionsFilter, 'create').callsFake( + nextSpy = vi.fn(); + vi.spyOn(exceptionsFilter, 'create').mockImplementation( () => ({ next: nextSpy, @@ -199,25 +209,23 @@ describe('RouterExplorer', () => { ); await handler(null!, null, null!); - expect(nextSpy.called).to.be.true; - expect(nextSpy.getCall(0).args[0]).to.be.instanceOf(Error); - expect(nextSpy.getCall(0).args[1]).to.be.instanceOf( - ExecutionContextHost, - ); + expect(nextSpy).toHaveBeenCalled(); + expect(nextSpy.mock.calls[0][0]).toBeInstanceOf(Error); + expect(nextSpy.mock.calls[0][1]).toBeInstanceOf(ExecutionContextHost); }); }); }); describe('applyVersionFilter', () => { it('should call and return the `applyVersionFilter` from the underlying http server', () => { - const router = sinon.spy(new NoopHttpAdapter({})); + const router = new NoopHttpAdapter({}); + const applyVersionFilterSpy = vi.spyOn(router, 'applyVersionFilter'); const routePathMetadata: RoutePathMetadata = { - methodVersion: - sinon.fake() as unknown as RoutePathMetadata['methodVersion'], + methodVersion: vi.fn() as unknown as RoutePathMetadata['methodVersion'], versioningOptions: - sinon.fake() as unknown as RoutePathMetadata['versioningOptions'], + vi.fn() as unknown as RoutePathMetadata['versioningOptions'], }; - const handler = sinon.stub(); + const handler = vi.fn(); // We're using type assertion here because `applyVersionFilter` is private const versionFilter = (routerBuilder as any).applyVersionFilter( @@ -226,17 +234,13 @@ describe('RouterExplorer', () => { handler, ); - expect( - router.applyVersionFilter.calledOnceWithExactly( - handler, - routePathMetadata.methodVersion!, - routePathMetadata.versioningOptions!, - ), - ).to.be.true; - - expect(router.applyVersionFilter.returnValues[0]).to.be.equal( - versionFilter, + expect(applyVersionFilterSpy).toHaveBeenCalledWith( + handler, + routePathMetadata.methodVersion!, + routePathMetadata.versioningOptions!, ); + + expect(applyVersionFilterSpy.mock.results[0].value).toBe(versionFilter); }); }); @@ -257,9 +261,9 @@ describe('RouterExplorer', () => { targetCallback, ); - expect( - Reflect.getMetadata('test_metadata_key', targetCallback), - ).to.be.equal('test_metadata_value'); + expect(Reflect.getMetadata('test_metadata_key', targetCallback)).toBe( + 'test_metadata_value', + ); }); }); }); diff --git a/packages/core/test/router/router-module.spec.ts b/packages/core/test/router/router-module.spec.ts index 8049d8b9655..3c71fc86a1e 100644 --- a/packages/core/test/router/router-module.spec.ts +++ b/packages/core/test/router/router-module.spec.ts @@ -1,12 +1,11 @@ -import { expect } from 'chai'; -import { ModulesContainer, NestContainer } from '../../injector'; -import { Module } from '../../injector/module'; -import { Routes } from '../../router/interfaces'; +import { ModulesContainer, NestContainer } from '../../injector/index.js'; +import { Module } from '../../injector/module.js'; +import { Routes } from '../../router/interfaces/index.js'; import { RouterModule, ROUTES, targetModulesByContainer, -} from '../../router/router-module'; +} from '../../router/router-module.js'; class TestModuleClass {} @@ -15,7 +14,7 @@ describe('RouterModule', () => { describe('register', () => { it('should return a dynamic module with routes registered as a provider', () => { - expect(RouterModule.register(routes)).to.deep.equal({ + expect(RouterModule.register(routes)).toEqual({ module: RouterModule, providers: [ { @@ -44,8 +43,9 @@ describe('RouterModule', () => { }, ]); - expect(targetModulesByContainer.get(container)!.has(moduleRef)).to.be - .true; + expect(targetModulesByContainer.get(container)!.has(moduleRef)).toBe( + true, + ); }); }); }); diff --git a/packages/core/test/router/router-proxy.spec.ts b/packages/core/test/router/router-proxy.spec.ts index 8b85bcd39fb..21716f399ac 100644 --- a/packages/core/test/router/router-proxy.spec.ts +++ b/packages/core/test/router/router-proxy.spec.ts @@ -1,26 +1,24 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { HttpException } from '../../../common/exceptions/http.exception'; -import { ExceptionsHandler } from '../../exceptions/exceptions-handler'; -import { ExecutionContextHost } from '../../helpers/execution-context-host'; -import { RouterProxy } from '../../router/router-proxy'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +import { HttpException } from '../../../common/exceptions/http.exception.js'; +import { ExceptionsHandler } from '../../exceptions/exceptions-handler.js'; +import { ExecutionContextHost } from '../../helpers/execution-context-host.js'; +import { RouterProxy } from '../../router/router-proxy.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('RouterProxy', () => { let routerProxy: RouterProxy; let handler: ExceptionsHandler; const httpException = new HttpException('test', 500); - let nextStub: sinon.SinonStub; + let nextStub: ReturnType; beforeEach(() => { handler = new ExceptionsHandler(new NoopHttpAdapter({})); - nextStub = sinon.stub(handler, 'next'); + nextStub = vi.spyOn(handler, 'next').mockImplementation(() => ({}) as any); routerProxy = new RouterProxy(); }); describe('createProxy', () => { it('should method return thunk', () => { const proxy = routerProxy.createProxy(() => {}, handler); - expect(typeof proxy === 'function').to.be.true; + expect(typeof proxy === 'function').toBe(true); }); it('should method encapsulate callback passed as argument', async () => { @@ -29,13 +27,11 @@ describe('RouterProxy', () => { }, handler); await proxy(null, null, null!); - expect(nextStub.calledOnce).to.be.true; - expect( - nextStub.calledWith( - httpException, - new ExecutionContextHost([null, null, null]), - ), - ).to.be.true; + expect(nextStub).toHaveBeenCalledOnce(); + expect(nextStub).toHaveBeenCalledWith( + httpException, + new ExecutionContextHost([null, null, null]), + ); }); it('should method encapsulate async callback passed as argument', async () => { @@ -45,20 +41,18 @@ describe('RouterProxy', () => { await proxy(null, null, null!); - expect(nextStub.calledOnce).to.be.true; - expect( - nextStub.calledWith( - httpException, - new ExecutionContextHost([null, null, null]), - ), - ).to.be.true; + expect(nextStub).toHaveBeenCalledOnce(); + expect(nextStub).toHaveBeenCalledWith( + httpException, + new ExecutionContextHost([null, null, null]), + ); }); }); describe('createExceptionLayerProxy', () => { it('should method return thunk', () => { const proxy = routerProxy.createExceptionLayerProxy(() => {}, handler); - expect(typeof proxy === 'function').to.be.true; + expect(typeof proxy === 'function').toBe(true); }); it('should method encapsulate callback passed as argument', async () => { @@ -70,13 +64,11 @@ describe('RouterProxy', () => { ); await proxy(null, null, null, null!); - expect(nextStub.calledOnce).to.be.true; - expect( - nextStub.calledWith( - httpException, - new ExecutionContextHost([null, null, null]), - ), - ).to.be.true; + expect(nextStub).toHaveBeenCalledOnce(); + expect(nextStub).toHaveBeenCalledWith( + httpException, + new ExecutionContextHost([null, null, null]), + ); }); it('should method encapsulate async callback passed as argument', async () => { @@ -89,13 +81,11 @@ describe('RouterProxy', () => { await proxy(null, null, null, null!); - expect(nextStub.calledOnce).to.be.true; - expect( - nextStub.calledWith( - httpException, - new ExecutionContextHost([null, null, null]), - ), - ).to.be.true; + expect(nextStub).toHaveBeenCalledOnce(); + expect(nextStub).toHaveBeenCalledWith( + httpException, + new ExecutionContextHost([null, null, null]), + ); }); }); }); diff --git a/packages/core/test/router/router-response-controller.spec.ts b/packages/core/test/router/router-response-controller.spec.ts index 1898fe6aa92..40b5eca4a3f 100644 --- a/packages/core/test/router/router-response-controller.spec.ts +++ b/packages/core/test/router/router-response-controller.spec.ts @@ -1,13 +1,11 @@ -import { isNil, isObject } from '@nestjs/common/utils/shared.utils'; -import { expect } from 'chai'; +import { isNil, isObject } from '@nestjs/common/utils/shared.utils.js'; import { IncomingMessage, ServerResponse } from 'http'; import { Observable, of, Subject } from 'rxjs'; -import * as sinon from 'sinon'; import { PassThrough, Writable } from 'stream'; -import { HttpStatus, RequestMethod } from '../../../common'; -import { RouterResponseController } from '../../router/router-response-controller'; -import { SseStream } from '../../router/sse-stream'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; +import { HttpStatus, RequestMethod } from '../../../common/index.js'; +import { RouterResponseController } from '../../router/router-response-controller.js'; +import { SseStream } from '../../router/sse-stream.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('RouterResponseController', () => { let adapter: NoopHttpAdapter; @@ -20,18 +18,17 @@ describe('RouterResponseController', () => { describe('apply', () => { let response: { - send: sinon.SinonSpy; - status?: sinon.SinonSpy; - json: sinon.SinonSpy; + send: ReturnType; + status?: ReturnType; + json: ReturnType; }; beforeEach(() => { - response = { send: sinon.spy(), json: sinon.spy(), status: sinon.spy() }; + response = { send: vi.fn(), json: vi.fn(), status: vi.fn() }; }); describe('when result is', () => { beforeEach(() => { - sinon - .stub(adapter, 'reply') - .callsFake((responseRef: any, body: any, statusCode?: number) => { + vi.spyOn(adapter, 'reply').mockImplementation( + (responseRef: any, body: any, statusCode?: number) => { if (statusCode) { responseRef.status(statusCode); } @@ -41,29 +38,30 @@ describe('RouterResponseController', () => { return isObject(body) ? responseRef.json(body) : responseRef.send(String(body)); - }); + }, + ); }); describe('nil', () => { it('should call send()', async () => { const value = null; await routerResponseController.apply(value, response, 200); - expect(response.send.called).to.be.true; + expect(response.send).toHaveBeenCalled(); }); }); describe('string', () => { it('should call send(value)', async () => { const value = 'string'; await routerResponseController.apply(value, response, 200); - expect(response.send.called).to.be.true; - expect(response.send.calledWith(String(value))).to.be.true; + expect(response.send).toHaveBeenCalled(); + expect(response.send).toHaveBeenCalledWith(String(value)); }); }); describe('object', () => { it('should call json(value)', async () => { const value = { test: 'test' }; await routerResponseController.apply(value, response, 200); - expect(response.json.called).to.be.true; - expect(response.json.calledWith(value)).to.be.true; + expect(response.json).toHaveBeenCalled(); + expect(response.json).toHaveBeenCalledWith(value); }); }); }); @@ -78,7 +76,7 @@ describe('RouterResponseController', () => { await routerResponseController.transformToResult( Promise.resolve(value), ), - ).to.be.eq(value); + ).toBe(value); }); }); @@ -89,25 +87,25 @@ describe('RouterResponseController', () => { await routerResponseController.transformToResult( of(1, 2, 3, lastValue), ), - ).to.be.eq(lastValue); + ).toBe(lastValue); }); }); describe('is an object that has the method `subscribe`', () => { it('should return a Promise that resolves to the input value', async () => { const value = { subscribe() {} }; - expect( - await routerResponseController.transformToResult(value), - ).to.equal(value); + expect(await routerResponseController.transformToResult(value)).toBe( + value, + ); }); }); describe('is an ordinary value', () => { it('should return a Promise that resolves to the input value', async () => { const value = 100; - expect( - await routerResponseController.transformToResult(value), - ).to.be.eq(value); + expect(await routerResponseController.transformToResult(value)).toBe( + value, + ); }); }); }); @@ -118,42 +116,44 @@ describe('RouterResponseController', () => { it('should return 201', () => { expect( routerResponseController.getStatusByMethod(RequestMethod.POST), - ).to.be.eql(201); + ).toEqual(201); }); }); describe('when RequestMethod is not POST', () => { it('should return 200', () => { expect( routerResponseController.getStatusByMethod(RequestMethod.GET), - ).to.be.eql(200); + ).toEqual(200); }); }); }); describe('render', () => { beforeEach(() => { - sinon - .stub(adapter, 'render') - .callsFake((response, view: string, options: any) => { + vi.spyOn(adapter, 'render').mockImplementation( + (response, view: string, options: any) => { return response.render(view, options); - }); + }, + ); }); it('should call "res.render()" with expected args', async () => { const template = 'template'; const value = 'test'; const result = Promise.resolve(value); - const response = { render: sinon.spy() }; + const response = { render: vi.fn() }; await routerResponseController.render(result, response, template); - expect(response.render.calledWith(template, value)).to.be.true; + expect(response.render).toHaveBeenCalledWith(template, value); }); }); describe('setHeaders', () => { - let setHeaderStub: sinon.SinonStub; + let setHeaderStub: ReturnType; beforeEach(() => { - setHeaderStub = sinon.stub(adapter, 'setHeader').callsFake(() => ({})); + setHeaderStub = vi + .spyOn(adapter, 'setHeader') + .mockImplementation(() => ({})); }); it('should set all custom headers', () => { @@ -161,17 +161,19 @@ describe('RouterResponseController', () => { const headers = [{ name: 'test', value: 'test_value' }]; routerResponseController.setHeaders(response, headers); - expect( - setHeaderStub.calledWith(response, headers[0].name, headers[0].value), - ).to.be.true; + expect(setHeaderStub).toHaveBeenCalledWith( + response, + headers[0].name, + headers[0].value, + ); }); }); describe('status', () => { - let statusStub: sinon.SinonStub; + let statusStub: ReturnType; beforeEach(() => { - statusStub = sinon.stub(adapter, 'status').callsFake(() => ({})); + statusStub = vi.spyOn(adapter, 'status').mockImplementation(() => ({})); }); it('should set status', () => { @@ -179,83 +181,85 @@ describe('RouterResponseController', () => { const statusCode = 400; routerResponseController.setStatus(response, statusCode); - expect(statusStub.calledWith(response, statusCode)).to.be.true; + expect(statusStub).toHaveBeenCalledWith(response, statusCode); }); }); describe('redirect should HttpServer.redirect', () => { it('should transformToResult', async () => { - const transformToResultSpy = sinon - .stub(routerResponseController, 'transformToResult') - .returns(Promise.resolve({ statusCode: 123, url: 'redirect url' })); + const transformToResultSpy = vi + .spyOn(routerResponseController, 'transformToResult') + .mockReturnValue( + Promise.resolve({ statusCode: 123, url: 'redirect url' }), + ); const result = {}; await routerResponseController.redirect(result, null, null!); - expect(transformToResultSpy.firstCall.args[0]).to.be.equal(result); + expect(transformToResultSpy.mock.calls[0][0]).toBe(result); }); it('should pass the response to redirect', async () => { - sinon - .stub(routerResponseController, 'transformToResult') - .returns(Promise.resolve({ statusCode: 123, url: 'redirect url' })); - const redirectSpy = sinon.spy(adapter, 'redirect'); + vi.spyOn(routerResponseController, 'transformToResult').mockReturnValue( + Promise.resolve({ statusCode: 123, url: 'redirect url' }), + ); + const redirectSpy = vi.spyOn(adapter, 'redirect'); const response = {}; await routerResponseController.redirect(null, response, null!); - expect(redirectSpy.firstCall.args[0]).to.be.equal(response); + expect(redirectSpy.mock.calls[0][0]).toBe(response); }); describe('status code', () => { it('should come from the transformed result if present', async () => { - sinon - .stub(routerResponseController, 'transformToResult') - .returns(Promise.resolve({ statusCode: 123, url: 'redirect url' })); - const redirectSpy = sinon.spy(adapter, 'redirect'); + vi.spyOn(routerResponseController, 'transformToResult').mockReturnValue( + Promise.resolve({ statusCode: 123, url: 'redirect url' }), + ); + const redirectSpy = vi.spyOn(adapter, 'redirect'); await routerResponseController.redirect(null, null, { statusCode: 999, url: 'not form here', }); - expect(redirectSpy.firstCall.args[1]).to.be.eql(123); + expect(redirectSpy.mock.calls[0][1]).toEqual(123); }); it('should come from the redirectResponse if not on the transformed result', async () => { - sinon - .stub(routerResponseController, 'transformToResult') - .returns(Promise.resolve({})); - const redirectSpy = sinon.spy(adapter, 'redirect'); + vi.spyOn(routerResponseController, 'transformToResult').mockReturnValue( + Promise.resolve({}), + ); + const redirectSpy = vi.spyOn(adapter, 'redirect'); await routerResponseController.redirect(null, null, { statusCode: 123, url: 'redirect url', }); - expect(redirectSpy.firstCall.args[1]).to.be.eql(123); + expect(redirectSpy.mock.calls[0][1]).toEqual(123); }); it('should default to HttpStatus.FOUND', async () => { - sinon - .stub(routerResponseController, 'transformToResult') - .returns(Promise.resolve({})); - const redirectSpy = sinon.spy(adapter, 'redirect'); + vi.spyOn(routerResponseController, 'transformToResult').mockReturnValue( + Promise.resolve({}), + ); + const redirectSpy = vi.spyOn(adapter, 'redirect'); await routerResponseController.redirect(null, null, { url: 'redirect url', }); - expect(redirectSpy.firstCall.args[1]).to.be.eql(HttpStatus.FOUND); + expect(redirectSpy.mock.calls[0][1]).toEqual(HttpStatus.FOUND); }); }); describe('url', () => { it('should come from the transformed result if present', async () => { - sinon - .stub(routerResponseController, 'transformToResult') - .returns(Promise.resolve({ statusCode: 123, url: 'redirect url' })); - const redirectSpy = sinon.spy(adapter, 'redirect'); + vi.spyOn(routerResponseController, 'transformToResult').mockReturnValue( + Promise.resolve({ statusCode: 123, url: 'redirect url' }), + ); + const redirectSpy = vi.spyOn(adapter, 'redirect'); await routerResponseController.redirect(null, null, { url: 'not from here', }); - expect(redirectSpy.firstCall.args[2]).to.be.eql('redirect url'); + expect(redirectSpy.mock.calls[0][2]).toEqual('redirect url'); }); it('should come from the redirectResponse if not on the transformed result', async () => { - sinon - .stub(routerResponseController, 'transformToResult') - .returns(Promise.resolve({})); - const redirectSpy = sinon.spy(adapter, 'redirect'); + vi.spyOn(routerResponseController, 'transformToResult').mockReturnValue( + Promise.resolve({}), + ); + const redirectSpy = vi.spyOn(adapter, 'redirect'); await routerResponseController.redirect(null, null, { statusCode: 123, url: 'redirect url', }); - expect(redirectSpy.firstCall.args[2]).to.be.eql('redirect url'); + expect(redirectSpy.mock.calls[0][2]).toEqual('redirect url'); }); }); }); @@ -269,7 +273,7 @@ describe('RouterResponseController', () => { {} as unknown as IncomingMessage, ); } catch (e) { - expect(e.message).to.eql( + expect(e.message).toEqual( 'You must return an Observable stream to use Server-Sent Events (SSE).', ); } @@ -308,7 +312,7 @@ describe('RouterResponseController', () => { ); request.destroy(); await written(response); - expect(response.content).to.eql( + expect(response.content).toEqual( ` id: 1 data: test @@ -320,8 +324,8 @@ data: test it('should use custom status code from response', async () => { class SinkWithStatusCode extends Writable { statusCode = 404; - writeHead = sinon.spy(); - flushHeaders = sinon.spy(); + writeHead = vi.fn(); + flushHeaders = vi.fn(); _write( chunk: any, @@ -341,7 +345,7 @@ data: test request as unknown as IncomingMessage, ); - expect(response.writeHead.firstCall.args[0]).to.equal(404); + expect(response.writeHead.mock.calls[0][0]).toBe(404); request.destroy(); }); @@ -378,7 +382,7 @@ data: test ); request.destroy(); await written(response); - expect(response.content).to.eql( + expect(response.content).toEqual( ` id: 1 data: test @@ -387,68 +391,70 @@ data: test ); }); - it('should close on request close', done => { - const result = of('test'); - const response = new Writable(); - response.end = () => done() as any; - response._write = () => {}; - - const request = new Writable(); - request._write = () => {}; - - void routerResponseController.sse( - result, - response as unknown as ServerResponse, - request as unknown as IncomingMessage, - ); - request.emit('close'); - }); - - it('should close the request when observable completes', done => { - const result = of('test'); - const response = new Writable(); - response.end = done as any; - response._write = () => {}; + it('should close on request close', () => + new Promise(done => { + const result = of('test'); + const response = new Writable(); + response.end = () => done() as any; + response._write = () => {}; - const request = new Writable(); - request._write = () => {}; + const request = new Writable(); + request._write = () => {}; - void routerResponseController.sse( - result, - response as unknown as ServerResponse, - request as unknown as IncomingMessage, - ); - }); + void routerResponseController.sse( + result, + response as unknown as ServerResponse, + request as unknown as IncomingMessage, + ); + request.emit('close'); + })); - it('should allow to intercept the response', done => { - const result = sinon.spy(); - const response = new Writable(); - response.end(); - response._write = () => {}; + it('should close the request when observable completes', () => + new Promise(done => { + const result = of('test'); + const response = new Writable(); + response.end = done as any; + response._write = () => {}; - const request = new Writable(); - request._write = () => {}; + const request = new Writable(); + request._write = () => {}; - try { void routerResponseController.sse( - result as unknown as Observable, + result, response as unknown as ServerResponse, request as unknown as IncomingMessage, ); - } catch { - // Whether an error is thrown or not - // is not relevant, so long as - // result is not called - } + })); - sinon.assert.notCalled(result); - done(); - }); + it('should allow to intercept the response', () => + new Promise(done => { + const result = vi.fn(); + const response = new Writable(); + response.end(); + response._write = () => {}; + + const request = new Writable(); + request._write = () => {}; + + try { + void routerResponseController.sse( + result as unknown as Observable, + response as unknown as ServerResponse, + request as unknown as IncomingMessage, + ); + } catch { + // Whether an error is thrown or not + // is not relevant, so long as + // result is not called + } + + expect(result).not.toHaveBeenCalled(); + done(); + })); describe('when writing data too densely', () => { const DEFAULT_MAX_LISTENERS = SseStream.defaultMaxListeners; const MAX_LISTENERS = 1; - const sandbox = sinon.createSandbox(); beforeEach(() => { // Can't access to the internal sseStream, @@ -457,18 +463,19 @@ data: test SseStream.defaultMaxListeners = MAX_LISTENERS; process.setMaxListeners(PROCESS_MAX_LISTENERS); - const sseStream = sinon.createStubInstance(SseStream); const originalWrite = SseStream.prototype.write; // Make `.write()` always return false, so as to listen `drain` event - sseStream.write.callsFake(function (...args: any[]) { + vi.spyOn(SseStream.prototype, 'write').mockImplementation(function ( + this: any, + ...args: any[] + ) { originalWrite.apply(this, args); return false; }); - sandbox.replace(SseStream.prototype, 'write', sseStream.write); }); afterEach(() => { - sandbox.restore(); + vi.restoreAllMocks(); SseStream.defaultMaxListeners = DEFAULT_MAX_LISTENERS; }); @@ -506,28 +513,29 @@ data: test await new Promise(resolve => process.nextTick(resolve)); - expect(maxDrainListenersExceededWarning).to.equal(null); + expect(maxDrainListenersExceededWarning).toBe(null); }); }); describe('when there is an error', () => { - it('should close the request', done => { - const result = new Subject(); - const response = new Writable(); - response.end = done as any; - response._write = () => {}; - - const request = new Writable(); - request._write = () => {}; - - void routerResponseController.sse( - result, - response as unknown as ServerResponse, - request as unknown as IncomingMessage, - ); + it('should close the request', () => + new Promise(done => { + const result = new Subject(); + const response = new Writable(); + response.end = done as any; + response._write = () => {}; + + const request = new Writable(); + request._write = () => {}; + + void routerResponseController.sse( + result, + response as unknown as ServerResponse, + request as unknown as IncomingMessage, + ); - result.error(new Error('Some error')); - }); + result.error(new Error('Some error')); + })); it('should write the error message to the stream', async () => { class Sink extends Writable { @@ -565,7 +573,7 @@ data: test request.destroy(); await written(response); - expect(response.content).to.eql( + expect(response.content).toEqual( ` event: error id: 1 diff --git a/packages/core/test/router/routes-resolver.spec.ts b/packages/core/test/router/routes-resolver.spec.ts index cbf035255f0..04d54a9bd85 100644 --- a/packages/core/test/router/routes-resolver.spec.ts +++ b/packages/core/test/router/routes-resolver.spec.ts @@ -1,24 +1,15 @@ -import { - BadRequestException, - HttpException, - Module, - Post, - VersioningType, -} from '@nestjs/common'; -import { MODULE_PATH } from '@nestjs/common/constants'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Controller } from '../../../common/decorators/core/controller.decorator'; -import { Get } from '../../../common/decorators/http/request-mapping.decorator'; -import { ApplicationConfig } from '../../application-config'; -import { NestContainer } from '../../injector'; -import { Injector } from '../../injector/injector'; -import { InstanceWrapper } from '../../injector/instance-wrapper'; -import { GraphInspector } from '../../inspector/graph-inspector'; -import { SerializedGraph } from '../../inspector/serialized-graph'; -import { RoutesResolver } from '../../router/routes-resolver'; -import { NoopHttpAdapter } from '../utils/noop-adapter.spec'; -import { createError as createFastifyError } from '@fastify/error'; +import { Module, Post, VersioningType } from '@nestjs/common'; +import { MODULE_PATH } from '@nestjs/common/constants.js'; +import { Controller } from '../../../common/decorators/core/controller.decorator.js'; +import { Get } from '../../../common/decorators/http/request-mapping.decorator.js'; +import { ApplicationConfig } from '../../application-config.js'; +import { NestContainer } from '../../injector/index.js'; +import { Injector } from '../../injector/injector.js'; +import { InstanceWrapper } from '../../injector/instance-wrapper.js'; +import { GraphInspector } from '../../inspector/graph-inspector.js'; +import { SerializedGraph } from '../../inspector/serialized-graph.js'; +import { RoutesResolver } from '../../router/routes-resolver.js'; +import { NoopHttpAdapter } from '../utils/noop-adapter.js'; describe('RoutesResolver', () => { @Controller('global') @@ -63,8 +54,8 @@ describe('RoutesResolver', () => { modules = new Map(); applicationRef = { use: () => ({}), - setNotFoundHandler: sinon.spy(), - setErrorHandler: sinon.spy(), + setNotFoundHandler: vi.fn(), + setErrorHandler: vi.fn(), } as any; container = { getModules: () => modules, @@ -98,16 +89,17 @@ describe('RoutesResolver', () => { routes.set('TestRoute', routeWrapper); const appInstance = new NoopHttpAdapter(router); - const exploreSpy = sinon.spy( + const exploreSpy = vi.spyOn( untypedRoutesResolver.routerExplorer, 'explore', ); const moduleName = ''; modules.set(moduleName, {}); - sinon - .stub(untypedRoutesResolver.routerExplorer, 'extractRouterPath') - .callsFake(() => ['']); + vi.spyOn( + untypedRoutesResolver.routerExplorer, + 'extractRouterPath', + ).mockImplementation(() => ['']); routesResolver.registerRouters(routes, moduleName, '', '', appInstance); const routePathMetadata = { @@ -119,16 +111,14 @@ describe('RoutesResolver', () => { methodVersion: undefined, methodPath: '/another-test', }; - expect(exploreSpy.called).to.be.true; - expect( - exploreSpy.calledWith( - routeWrapper, - moduleName, - appInstance, - undefined, - routePathMetadata, - ), - ).to.be.true; + expect(exploreSpy).toHaveBeenCalled(); + expect(exploreSpy).toHaveBeenCalledWith( + routeWrapper, + moduleName, + appInstance, + undefined, + routePathMetadata, + ); }); it('should register with host when specified', () => { @@ -140,16 +130,17 @@ describe('RoutesResolver', () => { routes.set('TestHostRoute', routeWrapper); const appInstance = new NoopHttpAdapter(router); - const exploreSpy = sinon.spy( + const exploreSpy = vi.spyOn( untypedRoutesResolver.routerExplorer, 'explore', ); const moduleName = ''; modules.set(moduleName, {}); - sinon - .stub(untypedRoutesResolver.routerExplorer, 'extractRouterPath') - .callsFake(() => ['']); + vi.spyOn( + untypedRoutesResolver.routerExplorer, + 'extractRouterPath', + ).mockImplementation(() => ['']); routesResolver.registerRouters(routes, moduleName, '', '', appInstance); const routePathMetadata = { @@ -162,16 +153,14 @@ describe('RoutesResolver', () => { methodPath: '/', }; - expect(exploreSpy.called).to.be.true; - expect( - exploreSpy.calledWith( - routeWrapper, - moduleName, - appInstance, - 'api.example.com', - routePathMetadata, - ), - ).to.be.true; + expect(exploreSpy).toHaveBeenCalled(); + expect(exploreSpy).toHaveBeenCalledWith( + routeWrapper, + moduleName, + appInstance, + 'api.example.com', + routePathMetadata, + ); }); it('should register with version when specified', () => { @@ -195,16 +184,17 @@ describe('RoutesResolver', () => { routes.set('TestVersionRoute', routeWrapper); const appInstance = new NoopHttpAdapter(router); - const exploreSpy = sinon.spy( + const exploreSpy = vi.spyOn( untypedRoutesResolver.routerExplorer, 'explore', ); const moduleName = ''; modules.set(moduleName, {}); - sinon - .stub(untypedRoutesResolver.routerExplorer, 'extractRouterPath') - .callsFake(() => ['']); + vi.spyOn( + untypedRoutesResolver.routerExplorer, + 'extractRouterPath', + ).mockImplementation(() => ['']); routesResolver.registerRouters(routes, moduleName, '', '', appInstance); const routePathMetadata = { @@ -219,16 +209,14 @@ describe('RoutesResolver', () => { methodPath: '/', }; - expect(exploreSpy.called).to.be.true; - expect( - exploreSpy.calledWith( - routeWrapper, - moduleName, - appInstance, - undefined, - routePathMetadata, - ), - ).to.be.true; + expect(exploreSpy).toHaveBeenCalled(); + expect(exploreSpy).toHaveBeenCalledWith( + routeWrapper, + moduleName, + appInstance, + undefined, + routePathMetadata, + ); }); }); @@ -245,12 +233,12 @@ describe('RoutesResolver', () => { modules.set('TestModule', { routes, metatype: class {} }); modules.set('TestModule2', { routes, metatype: class {} }); - const registerRoutersStub = sinon - .stub(routesResolver, 'registerRouters') - .callsFake(() => undefined); + const registerRoutersStub = vi + .spyOn(routesResolver, 'registerRouters') + .mockImplementation(() => undefined); - routesResolver.resolve({ use: sinon.spy() } as any, 'basePath'); - expect(registerRoutersStub.calledTwice).to.be.true; + routesResolver.resolve({ use: vi.fn() } as any, 'basePath'); + expect(registerRoutersStub).toHaveBeenCalledTimes(2); }); describe('registerRouters', () => { @@ -265,27 +253,15 @@ describe('RoutesResolver', () => { modules.set('TestModule', { routes, metatype: TestModule }); modules.set('TestModule2', { routes, metatype: TestModule2 }); - const spy = sinon - .stub(routesResolver, 'registerRouters') - .callsFake(() => undefined); + const spy = vi + .spyOn(routesResolver, 'registerRouters') + .mockImplementation(() => undefined); routesResolver.resolve(applicationRef, 'api/v1'); - expect( - spy - .getCall(0) - .calledWith(sinon.match.any, sinon.match.any, 'api/v1', '/test'), - ).to.be.true; - expect( - spy - .getCall(1) - .calledWith( - sinon.match.any, - sinon.match.any, - 'api/v1', - sinon.match.any, - ), - ).to.be.true; + expect(spy.mock.calls[0][2]).toBe('api/v1'); + expect(spy.mock.calls[0][3]).toBe('/test'); + expect(spy.mock.calls[1][2]).toBe('api/v1'); }); it('should register each module with the module path if present', () => { @@ -299,78 +275,17 @@ describe('RoutesResolver', () => { modules.set('TestModule', { routes, metatype: TestModule }); modules.set('TestModule2', { routes, metatype: TestModule2 }); - const spy = sinon - .stub(routesResolver, 'registerRouters') - .callsFake(() => undefined); + const spy = vi + .spyOn(routesResolver, 'registerRouters') + .mockImplementation(() => undefined); routesResolver.resolve(applicationRef, ''); - expect( - spy - .getCall(0) - .calledWith(sinon.match.any, sinon.match.any, '', '/test'), - ).to.be.true; + expect(spy.mock.calls[0][2]).toBe(''); + expect(spy.mock.calls[0][3]).toBe('/test'); // without module path - expect( - spy - .getCall(1) - .calledWith(sinon.match.any, sinon.match.any, '', undefined), - ).to.be.true; - }); - }); - }); - - describe('mapExternalExceptions', () => { - describe('when exception prototype is', () => { - describe('SyntaxError', () => { - it('should map to BadRequestException', () => { - const err = new SyntaxError(); - const outputErr = routesResolver.mapExternalException(err); - expect(outputErr).to.be.instanceof(BadRequestException); - }); - }); - describe('URIError', () => { - it('should map to BadRequestException', () => { - const err = new URIError(); - const outputErr = routesResolver.mapExternalException(err); - expect(outputErr).to.be.instanceof(BadRequestException); - }); - }); - describe('FastifyError', () => { - it('should map FastifyError with status code to HttpException', () => { - const FastifyErrorCls = createFastifyError( - 'FST_ERR_CTP_INVALID_MEDIA_TYPE', - 'Unsupported Media Type: %s', - 415, - ); - const error = new FastifyErrorCls(); - - const result = routesResolver.mapExternalException(error); - - expect(result).to.be.instanceOf(HttpException); - expect(result.message).to.equal(error.message); - expect(result.getStatus()).to.equal(415); - }); - - it('should return FastifyError without user status code to Internal Server Error HttpException', () => { - const FastifyErrorCls = createFastifyError( - 'FST_WITHOUT_STATUS_CODE', - 'Error without status code', - ); - const error = new FastifyErrorCls(); - - const result = routesResolver.mapExternalException(error); - expect(result).to.be.instanceOf(HttpException); - expect(result.message).to.equal(error.message); - expect(result.getStatus()).to.equal(500); - }); - }); - describe('other', () => { - it('should behave as an identity', () => { - const err = new Error(); - const outputErr = routesResolver.mapExternalException(err); - expect(outputErr).to.be.eql(err); - }); + expect(spy.mock.calls[1][2]).toBe(''); + expect(spy.mock.calls[1][3]).toBeUndefined(); }); }); }); @@ -379,7 +294,7 @@ describe('RoutesResolver', () => { it('should register not found handler', () => { routesResolver.registerNotFoundHandler(); - expect(applicationRef.setNotFoundHandler.called).to.be.true; + expect(applicationRef.setNotFoundHandler).toHaveBeenCalled(); }); }); @@ -387,7 +302,7 @@ describe('RoutesResolver', () => { it('should register exception handler', () => { routesResolver.registerExceptionHandler(); - expect(applicationRef.setErrorHandler.called).to.be.true; + expect(applicationRef.setErrorHandler).toHaveBeenCalled(); }); }); }); diff --git a/packages/core/test/router/sse-stream.spec.ts b/packages/core/test/router/sse-stream.spec.ts index 52666806efd..0ae09152ec2 100644 --- a/packages/core/test/router/sse-stream.spec.ts +++ b/packages/core/test/router/sse-stream.spec.ts @@ -1,9 +1,8 @@ -import { expect } from 'chai'; import { EventSource } from 'eventsource'; import { createServer, OutgoingHttpHeaders } from 'http'; import { AddressInfo } from 'net'; import { Writable } from 'stream'; -import { HeaderStream, SseStream } from '../../router/sse-stream'; +import { HeaderStream, SseStream } from '../../router/sse-stream.js'; const noop = () => {}; @@ -59,7 +58,7 @@ describe('SseStream', () => { sse.end(); await written(sink); - expect(sink.content).to.equal( + expect(sink.content).toBe( ` id: 1 data: hello @@ -87,7 +86,7 @@ data: monde sse.end(); await written(sink); - expect(sink.content).to.equal( + expect(sink.content).toBe( ` id: 1 data: {"hello":"world"} @@ -113,7 +112,7 @@ data: {"hello":"world"} sse.end(); await written(sink); - expect(sink.content).to.equal( + expect(sink.content).toBe( ` event: tea-time id: the-id @@ -124,89 +123,94 @@ data: hello ); }); - it('sets headers on destination when it looks like a HTTP Response', callback => { - const sse = new SseStream(); - const sink = new Sink( - (status: number, headers: string | OutgoingHttpHeaders) => { - expect(headers).to.deep.equal({ - 'Content-Type': 'text/event-stream', - Connection: 'keep-alive', - 'Cache-Control': - 'private, no-cache, no-store, must-revalidate, max-age=0, no-transform', - Pragma: 'no-cache', - Expire: '0', - 'X-Accel-Buffering': 'no', - }); - callback(); - return sink; - }, - ); - sse.pipe(sink); - }); - - it('sets additional headers when provided', callback => { - const sse = new SseStream(); - const sink = new Sink( - (status: number, headers: string | OutgoingHttpHeaders) => { - expect(headers).to.contain.keys('access-control-headers'); - expect(headers['access-control-headers']).to.equal('some-cors-value'); - callback(); - return sink; - }, - ); - - sse.pipe(sink, { - additionalHeaders: { 'access-control-headers': 'some-cors-value' }, - }); - }); - - it('sets custom status code when provided', callback => { - const sse = new SseStream(); - const sink = new Sink( - (status: number, headers: string | OutgoingHttpHeaders) => { - expect(status).to.equal(404); - callback(); - return sink; - }, - ); - - sse.pipe(sink, { - statusCode: 404, - }); - }); - - it('defaults to 200 status code when not provided', callback => { - const sse = new SseStream(); - const sink = new Sink( - (status: number, headers: string | OutgoingHttpHeaders) => { - expect(status).to.equal(200); - callback(); - return sink; - }, - ); - - sse.pipe(sink); - }); + it('sets headers on destination when it looks like a HTTP Response', () => + new Promise(callback => { + const sse = new SseStream(); + const sink = new Sink( + (status: number, headers: string | OutgoingHttpHeaders) => { + expect(headers).toEqual({ + 'Content-Type': 'text/event-stream', + Connection: 'keep-alive', + 'Cache-Control': + 'private, no-cache, no-store, must-revalidate, max-age=0, no-transform', + Pragma: 'no-cache', + Expire: '0', + 'X-Accel-Buffering': 'no', + }); + callback(); + return sink; + }, + ); + sse.pipe(sink); + })); + + it('sets additional headers when provided', () => + new Promise(callback => { + const sse = new SseStream(); + const sink = new Sink( + (status: number, headers: string | OutgoingHttpHeaders) => { + expect(headers).toHaveProperty('access-control-headers'); + expect(headers['access-control-headers']).toBe('some-cors-value'); + callback(); + return sink; + }, + ); - it('allows an eventsource to connect', callback => { - let sse: SseStream; - const server = createServer((req, res) => { - sse = new SseStream(req); - sse.pipe(res); - }); + sse.pipe(sink, { + additionalHeaders: { 'access-control-headers': 'some-cors-value' }, + }); + })); + + it('sets custom status code when provided', () => + new Promise(callback => { + const sse = new SseStream(); + const sink = new Sink( + (status: number, headers: string | OutgoingHttpHeaders) => { + expect(status).toBe(404); + callback(); + return sink; + }, + ); - server.listen(() => { - const es = new EventSource( - `http://localhost:${(server.address() as AddressInfo).port}`, + sse.pipe(sink, { + statusCode: 404, + }); + })); + + it('defaults to 200 status code when not provided', () => + new Promise(callback => { + const sse = new SseStream(); + const sink = new Sink( + (status: number, headers: string | OutgoingHttpHeaders) => { + expect(status).toBe(200); + callback(); + return sink; + }, ); - es.onmessage = e => { - expect(e.data).to.equal('hello'); - es.close(); - server.close(callback); - }; - es.onopen = () => sse.writeMessage({ data: 'hello' }, noop); - es.onerror = e => - callback(new Error(`Error from EventSource: ${JSON.stringify(e)}`)); - }); - }); + + sse.pipe(sink); + })); + + it('allows an eventsource to connect', () => + new Promise((resolve, reject) => { + let sse: SseStream; + const server = createServer((req, res) => { + sse = new SseStream(req); + sse.pipe(res); + }); + + server.listen(() => { + const es = new EventSource( + `http://localhost:${(server.address() as AddressInfo).port}`, + ); + es.onmessage = e => { + expect(e.data).toBe('hello'); + es.close(); + server.close(() => resolve()); + }; + es.onopen = () => sse.writeMessage({ data: 'hello' }, noop); + es.onerror = e => + reject(new Error(`Error from EventSource: ${JSON.stringify(e)}`)); + }); + })); }); diff --git a/packages/core/test/router/utils/flat-routes.spec.ts b/packages/core/test/router/utils/flat-routes.spec.ts index 8900afe541e..4796eb8079c 100644 --- a/packages/core/test/router/utils/flat-routes.spec.ts +++ b/packages/core/test/router/utils/flat-routes.spec.ts @@ -1,6 +1,5 @@ import { Module } from '@nestjs/common'; -import { expect } from 'chai'; -import { flattenRoutePaths } from '../../../router/utils'; +import { flattenRoutePaths } from '../../../router/utils/index.js'; describe('flattenRoutePaths', () => { it('should flatten all route paths', () => { @@ -127,6 +126,6 @@ describe('flattenRoutePaths', () => { { path: '/v3', module: AuthModule3 }, { path: '/v3', module: CatsModule3 }, ]; - expect(flattenRoutePaths(routes)).to.be.eql(expectedRoutes); + expect(flattenRoutePaths(routes)).toEqual(expectedRoutes); }); }); diff --git a/packages/core/test/scanner.spec.ts b/packages/core/test/scanner.spec.ts index f2c2835190f..05fbe07d9ec 100644 --- a/packages/core/test/scanner.spec.ts +++ b/packages/core/test/scanner.spec.ts @@ -1,23 +1,25 @@ import { Catch, Injectable } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { GUARDS_METADATA } from '../../common/constants'; -import { Controller } from '../../common/decorators/core/controller.decorator'; -import { UseGuards } from '../../common/decorators/core/use-guards.decorator'; -import { Module } from '../../common/decorators/modules/module.decorator'; -import { Scope } from '../../common/interfaces'; -import { ApplicationConfig } from '../application-config'; -import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '../constants'; -import { InvalidClassModuleException } from '../errors/exceptions/invalid-class-module.exception'; -import { InvalidModuleException } from '../errors/exceptions/invalid-module.exception'; -import { UndefinedModuleException } from '../errors/exceptions/undefined-module.exception'; -import { NestContainer } from '../injector/container'; -import { InstanceWrapper } from '../injector/instance-wrapper'; -import { GraphInspector } from '../inspector/graph-inspector'; -import { ModuleOverride } from '../interfaces/module-override.interface'; -import { MetadataScanner } from '../metadata-scanner'; -import { DependenciesScanner } from '../scanner'; -import Sinon = require('sinon'); +import { GUARDS_METADATA } from '../../common/constants.js'; +import { Controller } from '../../common/decorators/core/controller.decorator.js'; +import { UseGuards } from '../../common/decorators/core/use-guards.decorator.js'; +import { Module } from '../../common/decorators/modules/module.decorator.js'; +import { Scope } from '../../common/interfaces/index.js'; +import { ApplicationConfig } from '../application-config.js'; +import { + APP_FILTER, + APP_GUARD, + APP_INTERCEPTOR, + APP_PIPE, +} from '../constants.js'; +import { InvalidClassModuleException } from '../errors/exceptions/invalid-class-module.exception.js'; +import { InvalidModuleException } from '../errors/exceptions/invalid-module.exception.js'; +import { UndefinedModuleException } from '../errors/exceptions/undefined-module.exception.js'; +import { NestContainer } from '../injector/container.js'; +import { InstanceWrapper } from '../injector/instance-wrapper.js'; +import { GraphInspector } from '../inspector/graph-inspector.js'; +import { ModuleOverride } from '../interfaces/module-override.interface.js'; +import { MetadataScanner } from '../metadata-scanner.js'; +import { DependenciesScanner } from '../scanner.js'; describe('DependenciesScanner', () => { class Guard {} @@ -57,13 +59,11 @@ describe('DependenciesScanner', () => { let scanner: DependenciesScanner; let untypedScanner: any; - let mockContainer: sinon.SinonMock; let container: NestContainer; let graphInspector: GraphInspector; beforeEach(() => { container = new NestContainer(); - mockContainer = sinon.mock(container); graphInspector = new GraphInspector(container); scanner = new DependenciesScanner( @@ -73,47 +73,43 @@ describe('DependenciesScanner', () => { new ApplicationConfig(), ); untypedScanner = scanner as any; - sinon.stub(scanner, 'registerCoreModule').callsFake(async () => {}); + vi.spyOn(scanner, 'registerCoreModule').mockImplementation(async () => {}); }); afterEach(() => { - mockContainer.restore(); + vi.restoreAllMocks(); }); it('should "insertOrOverrideModule" call twice (2 modules) container method "addModule"', async () => { - const expectationCountAddModule = mockContainer - .expects('addModule') - .twice(); - const expectationCountReplaceModule = mockContainer - .expects('replaceModule') - .never(); + const addModuleSpy = vi.spyOn(container, 'addModule'); + const replaceModuleSpy = vi.spyOn(container, 'replaceModule'); await scanner.scan(TestModule); - expectationCountAddModule.verify(); - expectationCountReplaceModule.verify(); + expect(addModuleSpy).toHaveBeenCalledTimes(2); + expect(replaceModuleSpy).not.toHaveBeenCalled(); }); it('should "insertProvider" call twice (2 components) container method "addProvider"', async () => { - const expectation = mockContainer.expects('addProvider').twice(); - const stub = sinon.stub(scanner, 'insertExportedProviderOrModule'); + const addProviderSpy = vi.spyOn(container, 'addProvider'); + const stub = vi + .spyOn(scanner, 'insertExportedProviderOrModule') + .mockImplementation(() => ({}) as any); await scanner.scan(TestModule); - expectation.verify(); - stub.restore(); + expect(addProviderSpy).toHaveBeenCalledTimes(2); + stub.mockRestore(); }); it('should "insertController" call twice (2 components) container method "addController"', async () => { - const expectation = mockContainer.expects('addController').twice(); + const addControllerSpy = vi.spyOn(container, 'addController'); await scanner.scan(TestModule); - expectation.verify(); + expect(addControllerSpy).toHaveBeenCalledTimes(2); }); it('should "insertExportedProviderOrModule" call once (1 component) container method "addExportedProviderOrModule"', async () => { - const expectation = mockContainer - .expects('addExportedProviderOrModule') - .once(); + const addExportedSpy = vi.spyOn(container, 'addExportedProviderOrModule'); await scanner.scan(TestModule); - expectation.verify(); + expect(addExportedSpy).toHaveBeenCalledTimes(1); }); describe('when there is modules overrides', () => { @@ -168,42 +164,32 @@ describe('DependenciesScanner', () => { ]; it('should "putModule" call twice (2 modules) container method "replaceModule"', async () => { - const expectationReplaceModuleFirst = mockContainer - .expects('replaceModule') - .once() - .withArgs(OverwrittenModuleOne, OverrideModuleOne, sinon.match.array); - const expectationReplaceModuleSecond = mockContainer - .expects('replaceModule') - .once() - .withArgs(OverwrittenModuleTwo, OverrideModuleTwo, sinon.match.array); - const expectationCountAddModule = mockContainer - .expects('addModule') - .once(); + const replaceModuleSpy = vi.spyOn(container, 'replaceModule'); + const addModuleSpy = vi.spyOn(container, 'addModule'); await scanner.scan(OverrideTestModule, { overrides: modulesToOverride, }); - expectationReplaceModuleFirst.verify(); - expectationReplaceModuleSecond.verify(); - expectationCountAddModule.verify(); + expect(replaceModuleSpy).toHaveBeenCalledTimes(2); + expect(addModuleSpy).toHaveBeenCalledTimes(1); }); it('should "insertProvider" call once container method "addProvider"', async () => { - const expectation = mockContainer.expects('addProvider').once(); + const addProviderSpy = vi.spyOn(container, 'addProvider'); await scanner.scan(OverrideTestModule); - expectation.verify(); + expect(addProviderSpy).toHaveBeenCalled(); }); it('should "insertController" call twice (2 components) container method "addController"', async () => { - const expectation = mockContainer.expects('addController').twice(); + const addControllerSpy = vi.spyOn(container, 'addController'); await scanner.scan(OverrideTestModule); - expectation.verify(); + expect(addControllerSpy).toHaveBeenCalledTimes(2); }); it('should "putModule" call container method "replaceModule" with forwardRef() when forwardRef property exists', async () => { - const overwrittenForwardRefSpy = sinon.spy(); + const overwrittenForwardRefSpy = vi.fn(); @Module({}) class OverwrittenForwardRef {} @@ -216,7 +202,7 @@ describe('DependenciesScanner', () => { } } - const overrideForwardRefSpy = sinon.spy(); + const overrideForwardRefSpy = vi.fn(); @Module({}) class OverrideForwardRef {} @@ -243,40 +229,39 @@ describe('DependenciesScanner', () => { ], }); - expect(overwrittenForwardRefSpy.called).to.be.true; - expect(overrideForwardRefSpy.called).to.be.true; + expect(overwrittenForwardRefSpy).toHaveBeenCalled(); + expect(overrideForwardRefSpy).toHaveBeenCalled(); }); }); describe('reflectDynamicMetadata', () => { describe('when param has prototype', () => { it('should call "reflectParamInjectables" and "reflectInjectables"', () => { - const reflectInjectables = sinon - .stub(scanner, 'reflectInjectables') - .callsFake(() => undefined); + const reflectInjectables = vi + .spyOn(scanner, 'reflectInjectables') + .mockImplementation(() => undefined); - const reflectParamInjectables = sinon - .stub(scanner, 'reflectParamInjectables') - .callsFake(() => undefined); + const reflectParamInjectables = vi + .spyOn(scanner, 'reflectParamInjectables') + .mockImplementation(() => undefined); scanner.reflectDynamicMetadata({ prototype: true } as any, ''); - expect(reflectInjectables.called).to.be.true; - expect(reflectParamInjectables.called).to.be.true; + expect(reflectInjectables).toHaveBeenCalled(); + expect(reflectParamInjectables).toHaveBeenCalled(); }); }); describe('when param has not prototype', () => { it('should not call ""reflectParamInjectables" and "reflectInjectables"', () => { - const reflectInjectables = sinon - .stub(scanner, 'reflectInjectables') - .callsFake(() => undefined); - const reflectParamInjectables = sinon - .stub(scanner, 'reflectParamInjectables') - - .callsFake(() => undefined); + const reflectInjectables = vi + .spyOn(scanner, 'reflectInjectables') + .mockImplementation(() => undefined); + const reflectParamInjectables = vi + .spyOn(scanner, 'reflectParamInjectables') + .mockImplementation(() => undefined); scanner.reflectDynamicMetadata({} as any, ''); - expect(reflectInjectables.called).to.be.false; - expect(reflectParamInjectables.called).to.be.false; + expect(reflectInjectables).not.toHaveBeenCalled(); + expect(reflectParamInjectables).not.toHaveBeenCalled(); }); }); }); @@ -289,16 +274,16 @@ describe('DependenciesScanner', () => { const token = 'token'; const methodKey = 'methodKey'; - let addInjectableStub: Sinon.SinonStub; - let insertEnhancerMetadataCacheStub: Sinon.SinonStub; + let addInjectableStub: ReturnType; + let insertEnhancerMetadataCacheStub: ReturnType; beforeEach(() => { - addInjectableStub = sinon - .stub(untypedScanner.container, 'addInjectable') - .callsFake(() => instanceWrapper); - insertEnhancerMetadataCacheStub = sinon - .stub(graphInspector, 'insertEnhancerMetadataCache') - .callsFake(() => undefined); + addInjectableStub = vi + .spyOn(untypedScanner.container, 'addInjectable') + .mockImplementation(() => instanceWrapper); + insertEnhancerMetadataCacheStub = vi + .spyOn(graphInspector, 'insertEnhancerMetadataCache') + .mockImplementation(() => undefined); }); describe('when injectable is of type function', () => { @@ -314,20 +299,23 @@ describe('DependenciesScanner', () => { }); it('should call "addInjectable"', () => { - expect(addInjectableStub.calledWith(InjectableCls, token)).to.be.true; + expect(addInjectableStub).toHaveBeenCalledWith( + InjectableCls, + token, + subtype, + HostCls, + ); }); it('should call "insertEnhancerMetadataCache"', () => { - expect( - insertEnhancerMetadataCacheStub.calledWith({ - moduleToken: token, - classRef: HostCls, - enhancerInstanceWrapper: instanceWrapper, - targetNodeId: instanceWrapper.id, - methodKey, - subtype, - }), - ).to.be.true; + expect(insertEnhancerMetadataCacheStub).toHaveBeenCalledWith({ + moduleToken: token, + classRef: HostCls, + enhancerInstanceWrapper: instanceWrapper, + targetNodeId: instanceWrapper.id, + methodKey, + subtype, + }); }); }); describe('when injectable is not of type function', () => { @@ -345,19 +333,17 @@ describe('DependenciesScanner', () => { }); it('should not call "addInjectable"', () => { - expect(addInjectableStub.notCalled).to.be.true; + expect(addInjectableStub).not.toHaveBeenCalled(); }); it('should call "insertEnhancerMetadataCache"', () => { - expect( - insertEnhancerMetadataCacheStub.calledWith({ - moduleToken: token, - classRef: HostCls, - enhancerRef: injectableRef, - methodKey, - subtype, - }), - ).to.be.true; + expect(insertEnhancerMetadataCacheStub).toHaveBeenCalledWith({ + moduleToken: token, + classRef: HostCls, + enhancerRef: injectableRef, + methodKey, + subtype, + }); }); }); }); @@ -372,7 +358,7 @@ describe('DependenciesScanner', () => { describe('reflectKeyMetadata', () => { it('should return undefined', () => { const result = scanner.reflectKeyMetadata(TestComponent, 'key', 'method'); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); it('should return an array that consists of 1 element', () => { const methodKey = 'method'; @@ -381,7 +367,7 @@ describe('DependenciesScanner', () => { GUARDS_METADATA, methodKey, ); - expect(result).to.be.deep.equal({ methodKey, metadata: [Guard] }); + expect(result).toEqual({ methodKey, metadata: [Guard] }); }); it('should return an array that consists of 2 elements', () => { const methodKey = 'method2'; @@ -390,49 +376,49 @@ describe('DependenciesScanner', () => { GUARDS_METADATA, methodKey, ); - expect(result).to.be.deep.equal({ methodKey, metadata: [Guard, Guard] }); + expect(result).toEqual({ methodKey, metadata: [Guard, Guard] }); }); }); describe('insertModule', () => { it('should call forwardRef() when forwardRef property exists', async () => { - sinon.stub(container, 'addModule').returns({} as any); + vi.spyOn(container, 'addModule').mockReturnValue({} as any); - const module = { forwardRef: sinon.stub().returns(class {}) }; + const module = { forwardRef: vi.fn().mockReturnValue(class {}) }; await scanner.insertModule(module, []); - expect(module.forwardRef.called).to.be.true; + expect(module.forwardRef).toHaveBeenCalled(); }); - it('should throw "InvalidClassModuleException" exception when supplying a class annotated with `@Injectable()` decorator', () => { - sinon.stub(container, 'addModule').returns({} as any); + it('should throw "InvalidClassModuleException" exception when supplying a class annotated with `@Injectable()` decorator', async () => { + vi.spyOn(container, 'addModule').mockReturnValue({} as any); - expect(scanner.insertModule(TestComponent, [])).to.be.rejectedWith( + await expect(scanner.insertModule(TestComponent, [])).rejects.toThrow( InvalidClassModuleException, ); }); - it('should throw "InvalidClassModuleException" exception when supplying a class annotated with `@Controller()` decorator', () => { - sinon.stub(container, 'addModule').returns({} as any); + it('should throw "InvalidClassModuleException" exception when supplying a class annotated with `@Controller()` decorator', async () => { + vi.spyOn(container, 'addModule').mockReturnValue({} as any); - expect(scanner.insertModule(TestController, [])).to.be.rejectedWith( + await expect(scanner.insertModule(TestController, [])).rejects.toThrow( InvalidClassModuleException, ); }); - it('should throw "InvalidClassModuleException" exception when supplying a class annotated with (only) `@Catch()` decorator', () => { - sinon.stub(container, 'addModule').returns({} as any); + it('should throw "InvalidClassModuleException" exception when supplying a class annotated with (only) `@Catch()` decorator', async () => { + vi.spyOn(container, 'addModule').mockReturnValue({} as any); - expect( + await expect( scanner.insertModule(TestExceptionFilterWithoutInjectable, []), - ).to.be.rejectedWith(InvalidClassModuleException); + ).rejects.toThrow(InvalidClassModuleException); }); }); describe('insertImport', () => { it('should call forwardRef() when forwardRef property exists', async () => { - const module = { forwardRef: sinon.stub().returns({}) }; + const module = { forwardRef: vi.fn().mockReturnValue({}) }; - sinon.stub(container, 'addImport').returns({} as any); + vi.spyOn(container, 'addImport').mockReturnValue({} as any); await scanner.insertImport(module, [] as any, 'test'); - expect(module.forwardRef.called).to.be.true; + expect(module.forwardRef).toHaveBeenCalled(); }); describe('when "related" is nil', () => { it('should throw exception', async () => { @@ -442,7 +428,7 @@ describe('DependenciesScanner', () => { } catch (e) { error = e; } - expect(error).to.not.be.undefined; + expect(error).not.toBeUndefined(); }); }); }); @@ -453,14 +439,13 @@ describe('DependenciesScanner', () => { describe('when provider is not custom', () => { it('should call container "addProvider" with expected args', () => { const provider = {}; - const expectation = mockContainer - .expects('addProvider') - .withArgs(provider, token); + const addProviderSpy = vi + .spyOn(container, 'addProvider') + .mockImplementation(() => false as any); - mockContainer.expects('addProvider').callsFake(() => false); scanner.insertProvider(provider as any, token); - expectation.verify(); + expect(addProviderSpy).toHaveBeenCalledWith(provider, token); }); }); describe('when provider is custom', () => { @@ -471,20 +456,23 @@ describe('DependenciesScanner', () => { }; it('should call container "addProvider" with expected args', () => { - const expectation = mockContainer.expects('addProvider').atLeast(1); + const addProviderSpy = vi + .spyOn(container, 'addProvider') + .mockImplementation(() => false as any); - mockContainer.expects('addProvider').callsFake(() => false); scanner.insertProvider(provider, token); - expectation.verify(); + expect(addProviderSpy).toHaveBeenCalled(); }); it('should push new object to "applicationProvidersApplyMap" array', () => { - mockContainer.expects('addProvider').callsFake(() => false); + vi.spyOn(container, 'addProvider').mockImplementation( + () => false as any, + ); scanner.insertProvider(provider, token); const applyMap = untypedScanner.applicationProvidersApplyMap; - expect(applyMap).to.have.length(1); - expect(applyMap[0].moduleKey).to.be.eql(token); + expect(applyMap).toHaveLength(1); + expect(applyMap[0].moduleKey).toEqual(token); }); }); describe('and is global and request/transient scoped', () => { @@ -494,12 +482,13 @@ describe('DependenciesScanner', () => { scope: Scope.REQUEST, }; it('should call container "addInjectable" with expected args', () => { - const expectation = mockContainer.expects('addInjectable').atLeast(1); + const addInjectableSpy = vi + .spyOn(container, 'addInjectable') + .mockImplementation(() => false as any); - mockContainer.expects('addInjectable').callsFake(() => false); scanner.insertProvider(provider, token); - expectation.verify(); + expect(addInjectableSpy).toHaveBeenCalled(); }); }); describe('and is not global', () => { @@ -508,21 +497,22 @@ describe('DependenciesScanner', () => { useValue: true, }; it('should call container "addProvider" with expected args', () => { - const expectation = mockContainer - .expects('addProvider') - .withArgs(component, token); + const addProviderSpy = vi + .spyOn(container, 'addProvider') + .mockImplementation(() => false as any); - mockContainer.expects('addProvider').callsFake(() => false); scanner.insertProvider(component, token); - expectation.verify(); + expect(addProviderSpy).toHaveBeenCalledWith(component, token); }); it('should not push new object to "applicationProvidersApplyMap" array', () => { - expect(untypedScanner.applicationProvidersApplyMap).to.have.length(0); + expect(untypedScanner.applicationProvidersApplyMap).toHaveLength(0); - mockContainer.expects('addProvider').callsFake(() => false); + vi.spyOn(container, 'addProvider').mockImplementation( + () => false as any, + ); scanner.insertProvider(component, token); - expect(untypedScanner.applicationProvidersApplyMap).to.have.length(0); + expect(untypedScanner.applicationProvidersApplyMap).toHaveLength(0); }); }); }); @@ -540,27 +530,29 @@ describe('DependenciesScanner', () => { const instanceWrapper = { instance: expectedInstance, } as unknown as InstanceWrapper; - mockContainer.expects('getModules').callsFake(() => ({ - get: () => ({ - providers: { get: () => instanceWrapper }, - }), - })); + vi.spyOn(container, 'getModules').mockImplementation( + () => + ({ + get: () => ({ + providers: { get: () => instanceWrapper }, + }), + }) as any, + ); - const applySpy = sinon.spy(); - sinon.stub(scanner, 'getApplyProvidersMap').callsFake(() => ({ + const applySpy = vi.fn(); + vi.spyOn(scanner, 'getApplyProvidersMap').mockImplementation(() => ({ [provider.type]: applySpy, })); - const insertAttachedEnhancerStub = sinon.stub( - graphInspector, - 'insertAttachedEnhancer', - ); + const insertAttachedEnhancerStub = vi + .spyOn(graphInspector, 'insertAttachedEnhancer') + .mockImplementation(() => {}); scanner.applyApplicationProviders(); - expect(applySpy.called).to.be.true; - expect(applySpy.calledWith(expectedInstance)).to.be.true; - expect(insertAttachedEnhancerStub.calledWith(instanceWrapper)).to.be.true; + expect(applySpy).toHaveBeenCalled(); + expect(applySpy).toHaveBeenCalledWith(expectedInstance); + expect(insertAttachedEnhancerStub).toHaveBeenCalledWith(instanceWrapper); }); it('should apply each globally scoped provider', () => { const provider = { @@ -572,28 +564,33 @@ describe('DependenciesScanner', () => { untypedScanner.applicationProvidersApplyMap = [provider]; const expectedInstanceWrapper = new InstanceWrapper(); - mockContainer.expects('getModules').callsFake(() => ({ - get: () => ({ - injectables: { get: () => expectedInstanceWrapper }, - }), - })); - - const applySpy = sinon.spy(); - sinon.stub(scanner, 'getApplyRequestProvidersMap').callsFake(() => ({ - [provider.type]: applySpy, - })); + vi.spyOn(container, 'getModules').mockImplementation( + () => + ({ + get: () => ({ + injectables: { get: () => expectedInstanceWrapper }, + }), + }) as any, + ); - const insertAttachedEnhancerStub = sinon.stub( - graphInspector, - 'insertAttachedEnhancer', + const applySpy = vi.fn(); + vi.spyOn(scanner, 'getApplyRequestProvidersMap').mockImplementation( + () => ({ + [provider.type]: applySpy, + }), ); + const insertAttachedEnhancerStub = vi + .spyOn(graphInspector, 'insertAttachedEnhancer') + .mockImplementation(() => {}); + scanner.applyApplicationProviders(); - expect(applySpy.called).to.be.true; - expect(applySpy.calledWith(expectedInstanceWrapper)).to.be.true; - expect(insertAttachedEnhancerStub.calledWith(expectedInstanceWrapper)).to - .be.true; + expect(applySpy).toHaveBeenCalled(); + expect(applySpy).toHaveBeenCalledWith(expectedInstanceWrapper); + expect(insertAttachedEnhancerStub).toHaveBeenCalledWith( + expectedInstanceWrapper, + ); }); }); @@ -619,115 +616,118 @@ describe('DependenciesScanner', () => { controllers.set('test', fakeController); providers.set(providerToken, fakeProvider); - mockContainer.expects('getModules').callsFake(() => ({ - get: () => ({ - injectables: { get: () => instance }, - controllers, - entryProviders: Array.from(providers.values()), - }), - values() { - return [this.get()]; - }, - })); + vi.spyOn(container, 'getModules').mockImplementation( + () => + ({ + get: () => ({ + injectables: { get: () => instance }, + controllers, + entryProviders: Array.from(providers.values()), + }), + values() { + return [this.get()]; + }, + }) as any, + ); - const addEnhancerMetadataControllerSpy = sinon.spy( + const addEnhancerMetadataControllerSpy = vi.spyOn( fakeController, 'addEnhancerMetadata', ); - const addEnhancerMetadataProviderSpy = sinon.spy( + const addEnhancerMetadataProviderSpy = vi.spyOn( fakeProvider, 'addEnhancerMetadata', ); scanner.addScopedEnhancersMetadata(); - expect(addEnhancerMetadataControllerSpy.called).to.be.true; - expect(addEnhancerMetadataControllerSpy.calledWith(instance)).to.be.true; - expect(addEnhancerMetadataProviderSpy.called).to.be.true; - expect(addEnhancerMetadataProviderSpy.calledWith(instance)).to.be.true; + expect(addEnhancerMetadataControllerSpy).toHaveBeenCalled(); + expect(addEnhancerMetadataControllerSpy).toHaveBeenCalledWith(instance); + expect(addEnhancerMetadataProviderSpy).toHaveBeenCalled(); + expect(addEnhancerMetadataProviderSpy).toHaveBeenCalledWith(instance); }); }); describe('getApplyProvidersMap', () => { describe(`when token is ${APP_INTERCEPTOR}`, () => { it('call "addGlobalInterceptor"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalInterceptor', ); scanner.getApplyProvidersMap()[APP_INTERCEPTOR](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); describe(`when token is ${APP_GUARD}`, () => { it('call "addGlobalGuard"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalGuard', ); scanner.getApplyProvidersMap()[APP_GUARD](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); describe(`when token is ${APP_PIPE}`, () => { it('call "addGlobalPipe"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalPipe', ); scanner.getApplyProvidersMap()[APP_PIPE](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); describe(`when token is ${APP_FILTER}`, () => { it('call "addGlobalFilter"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalFilter', ); scanner.getApplyProvidersMap()[APP_FILTER](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); }); describe('getApplyRequestProvidersMap', () => { describe(`when token is ${APP_INTERCEPTOR}`, () => { it('call "addGlobalRequestInterceptor"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalRequestInterceptor', ); scanner.getApplyRequestProvidersMap()[APP_INTERCEPTOR](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); describe(`when token is ${APP_GUARD}`, () => { it('call "addGlobalRequestGuard"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalRequestGuard', ); scanner.getApplyRequestProvidersMap()[APP_GUARD](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); describe(`when token is ${APP_PIPE}`, () => { it('call "addGlobalRequestPipe"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalRequestPipe', ); scanner.getApplyRequestProvidersMap()[APP_PIPE](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); describe(`when token is ${APP_FILTER}`, () => { it('call "addGlobalRequestFilter"', () => { - const addSpy = sinon.spy( + const addSpy = vi.spyOn( untypedScanner.applicationConfig, 'addGlobalRequestFilter', ); scanner.getApplyRequestProvidersMap()[APP_FILTER](null); - expect(addSpy.called).to.be.true; + expect(addSpy).toHaveBeenCalled(); }); }); }); @@ -739,7 +739,7 @@ describe('DependenciesScanner', () => { scope: [UndefinedModule], }); } catch (exception) { - expect(exception instanceof UndefinedModuleException).to.be.true; + expect(exception instanceof UndefinedModuleException).toBe(true); } }); it('should throw an exception when the imports array includes an invalid value', async () => { @@ -749,7 +749,7 @@ describe('DependenciesScanner', () => { scope: [InvalidModule], }); } catch (exception) { - expect(exception instanceof InvalidModuleException).to.be.true; + expect(exception instanceof InvalidModuleException).toBe(true); } }); }); diff --git a/packages/core/test/services/reflector.service.spec.ts b/packages/core/test/services/reflector.service.spec.ts index 000c931adf9..1703f583487 100644 --- a/packages/core/test/services/reflector.service.spec.ts +++ b/packages/core/test/services/reflector.service.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { Reflector } from '../../services/reflector.service'; +import { Reflector } from '../../services/reflector.service.js'; const transformDecorator = Reflector.createDecorator({ transform: value => value.length, @@ -31,7 +30,7 @@ describe('Reflector', () => { it('should reflect metadata by key', () => { const value = 'value'; Reflect.defineMetadata(key, value, Test1); - expect(reflector.get(key, Test1)).to.eql(value); + expect(reflector.get(key, Test1)).toEqual(value); }); it('should reflect metadata by decorator', () => { @@ -41,7 +40,7 @@ describe('Reflector', () => { // string let reflectedValue = reflector.get(decorator, Test1); - expect(reflectedValue).to.eql(value); + expect(reflectedValue).toEqual(value); // @ts-expect-error 'value' is not assignable to parameter of type 'string' reflectedValue = true; @@ -56,7 +55,7 @@ describe('Reflector', () => { // string[] let reflectedValue = reflector.get(decorator, Test1); - expect(reflectedValue).to.eql(value); + expect(reflectedValue).toEqual(value); // @ts-expect-error 'value' is not assignable to parameter of type 'string[]' reflectedValue = true; @@ -66,7 +65,7 @@ describe('Reflector', () => { it('should reflect metadata by decorator (with transform option)', () => { let reflectedValue = reflector.get(transformDecorator, TestTransform); - expect(reflectedValue).to.eql(3); + expect(reflectedValue).toEqual(3); // @ts-expect-error 'value' is not assignable to type 'number' reflectedValue = []; @@ -86,7 +85,7 @@ describe('Reflector', () => { const value2 = 'value2'; Reflect.defineMetadata(key, value1, Test1); Reflect.defineMetadata(key, value2, Test2); - expect(reflector.getAll(key, [Test1, Test2])).to.eql([value1, value2]); + expect(reflector.getAll(key, [Test1, Test2])).toEqual([value1, value2]); }); it('should reflect metadata of all targets by decorator', () => { const decorator = Reflector.createDecorator(); @@ -97,7 +96,7 @@ describe('Reflector', () => { // string[] const reflectedValue = reflector.getAll(decorator, [Test1, Test2]); - expect(reflectedValue).to.eql([value1, value2]); + expect(reflectedValue).toEqual([value1, value2]); reflectedValue satisfies string[]; }); @@ -105,7 +104,7 @@ describe('Reflector', () => { describe('getAllAndMerge', () => { it('should return an empty array when there are no targets', () => { - expect(reflector.getAllAndMerge(key, [])).to.be.empty; + expect(reflector.getAllAndMerge(key, [])).toHaveLength(0); }); it('should reflect metadata of all targets and concat arrays', () => { const decorator = Reflector.createDecorator(); @@ -117,7 +116,7 @@ describe('Reflector', () => { Test1, Test1, ]); - expect(reflectedValue).to.eql([value, value]); + expect(reflectedValue).toEqual([value, value]); reflectedValue satisfies string[]; }); @@ -131,7 +130,7 @@ describe('Reflector', () => { Test1, Test1, ]); - expect(reflectedValue).to.eql([value, value]); + expect(reflectedValue).toEqual([value, value]); reflectedValue satisfies boolean[]; }); @@ -145,7 +144,7 @@ describe('Reflector', () => { Test1, Test1, ]); - expect(reflectedValue).to.eql([value, value]); + expect(reflectedValue).toEqual([value, value]); reflectedValue satisfies string[]; }); @@ -161,7 +160,7 @@ describe('Reflector', () => { Test1, Test2, ]); - expect(reflectedValue).to.eql({ + expect(reflectedValue).toEqual({ ...value1, ...value2, }); @@ -173,19 +172,19 @@ describe('Reflector', () => { Reflect.defineMetadata(key, value, Test1); const result = reflector.getAllAndMerge(key, [Test1, Test2]); - expect(result).to.eql([value]); + expect(result).toEqual([value]); result satisfies string[]; }); it('should reflect metadata of all targets and return a single array unmodified', () => { const value = ['value']; Reflect.defineMetadata(key, value, Test1); - expect(reflector.getAllAndMerge(key, [Test1, Test2])).to.eql(value); + expect(reflector.getAllAndMerge(key, [Test1, Test2])).toEqual(value); }); it('should reflect metadata of all targets and return a single object unmodified', () => { const value = { test: 'value' }; Reflect.defineMetadata(key, value, Test1); - expect(reflector.getAllAndMerge(key, [Test1, Test2])).to.eql(value); + expect(reflector.getAllAndMerge(key, [Test1, Test2])).toEqual(value); }); }); @@ -195,7 +194,52 @@ describe('Reflector', () => { const value2 = 'value2'; Reflect.defineMetadata(key, value1, Test1); Reflect.defineMetadata(key, value2, Test2); - expect(reflector.getAllAndOverride(key, [Test1, Test2])).to.eql(value1); + expect(reflector.getAllAndOverride(key, [Test1, Test2])).toEqual(value1); + }); + + it('should return undefined when no target has metadata', () => { + expect(reflector.getAllAndOverride(key, [Test1, Test2])).toBeUndefined(); + }); + + it('should skip undefined and return the first defined value', () => { + Reflect.defineMetadata(key, 'second', Test2); + expect(reflector.getAllAndOverride(key, [Test1, Test2])).toEqual( + 'second', + ); + }); + + it('should work with a decorator instead of a string key', () => { + const decorator = Reflector.createDecorator(); + Reflect.defineMetadata(decorator.KEY, 'decorated', Test1); + expect(reflector.getAllAndOverride(decorator, [Test1])).toEqual( + 'decorated', + ); + }); + }); + + describe('getAll (edge cases)', () => { + it('should return array of undefined for targets without metadata', () => { + expect(reflector.getAll(key, [Test1, Test2])).toEqual([ + undefined, + undefined, + ]); + }); + + it('should handle null targets gracefully', () => { + expect(reflector.getAll(key, null as any)).toEqual([]); + }); + }); + + describe('getAllAndMerge (edge cases)', () => { + it('should use [a, b] fallback when values are not arrays or objects', () => { + Reflect.defineMetadata(key, 42, Test1); + Reflect.defineMetadata(key, 99, Test2); + expect(reflector.getAllAndMerge(key, [Test1, Test2])).toEqual([42, 99]); + }); + + it('should return empty array when all metadata is undefined', () => { + const result = reflector.getAllAndMerge(key, [Test1, Test2]); + expect(result).toEqual([]); }); }); }); diff --git a/packages/core/test/utils/noop-adapter.spec.ts b/packages/core/test/utils/noop-adapter.ts similarity index 91% rename from packages/core/test/utils/noop-adapter.spec.ts rename to packages/core/test/utils/noop-adapter.ts index 8cc46b042d6..d9e305b3a62 100644 --- a/packages/core/test/utils/noop-adapter.spec.ts +++ b/packages/core/test/utils/noop-adapter.ts @@ -1,6 +1,6 @@ import { RequestMethod, VersioningOptions } from '@nestjs/common'; -import { VersionValue } from '@nestjs/common/interfaces'; -import { AbstractHttpAdapter } from '../../adapters'; +import { VersionValue } from '@nestjs/common/interfaces/index.js'; +import { AbstractHttpAdapter } from '../../adapters/index.js'; export class NoopHttpAdapter extends AbstractHttpAdapter { constructor(instance: any) { diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json index 99c13372dc8..928f71bc6d6 100644 --- a/packages/core/tsconfig.build.json +++ b/packages/core/tsconfig.build.json @@ -2,15 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/microservices": ["../microservices"], - "@nestjs/microservices/*": ["../microservices/*"], - "@nestjs/websockets": ["../websockets"], - "@nestjs/websockets/*": ["../websockets/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/microservices/client/client-grpc.ts b/packages/microservices/client/client-grpc.ts index 7595a09a47a..4f717bab698 100644 --- a/packages/microservices/client/client-grpc.ts +++ b/packages/microservices/client/client-grpc.ts @@ -1,15 +1,15 @@ -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isFunction, isObject } from '@nestjs/common/utils/shared.utils'; +import { createRequire } from 'module'; import { Observable, Subscription } from 'rxjs'; -import { GRPC_DEFAULT_PROTO_LOADER, GRPC_DEFAULT_URL } from '../constants'; -import { InvalidGrpcPackageException } from '../errors/invalid-grpc-package.exception'; -import { InvalidGrpcServiceException } from '../errors/invalid-grpc-service.exception'; -import { InvalidProtoDefinitionException } from '../errors/invalid-proto-definition.exception'; -import { ChannelOptions } from '../external/grpc-options.interface'; -import { getGrpcPackageDefinition } from '../helpers'; -import { ClientGrpc, GrpcOptions } from '../interfaces'; -import { ClientProxy } from './client-proxy'; +import { GRPC_DEFAULT_PROTO_LOADER, GRPC_DEFAULT_URL } from '../constants.js'; +import { InvalidGrpcPackageException } from '../errors/invalid-grpc-package.exception.js'; +import { InvalidGrpcServiceException } from '../errors/invalid-grpc-service.exception.js'; +import { InvalidProtoDefinitionException } from '../errors/invalid-proto-definition.exception.js'; +import { ChannelOptions } from '../external/grpc-options.interface.js'; +import { getGrpcPackageDefinition } from '../helpers/index.js'; +import { ClientGrpc, GrpcOptions } from '../interfaces/index.js'; +import { ClientProxy } from './client-proxy.js'; +import { Logger } from '@nestjs/common'; +import { loadPackageSync, isFunction, isObject } from '@nestjs/common/internal'; const GRPC_CANCELLED = 'Cancelled'; @@ -50,18 +50,12 @@ export class ClientGrpcProxy const protoLoader = this.getOptionsProp(options, 'protoLoader') || GRPC_DEFAULT_PROTO_LOADER; - grpcPackage = loadPackage('@grpc/grpc-js', ClientGrpcProxy.name, () => - require('@grpc/grpc-js'), + grpcPackage = loadPackageSync('@grpc/grpc-js', ClientGrpcProxy.name, () => + createRequire(import.meta.url)('@grpc/grpc-js'), ); - grpcProtoLoaderPackage = loadPackage( - protoLoader, - ClientGrpcProxy.name, - () => - protoLoader === GRPC_DEFAULT_PROTO_LOADER - ? require('@grpc/proto-loader') - : require(protoLoader), - ); + grpcProtoLoaderPackage = loadPackageSync(protoLoader, ClientGrpcProxy.name); + this.grpcClients = this.createClients(); } diff --git a/packages/microservices/client/client-kafka.ts b/packages/microservices/client/client-kafka.ts index eaffbca552a..a2b87cc209e 100644 --- a/packages/microservices/client/client-kafka.ts +++ b/packages/microservices/client/client-kafka.ts @@ -1,6 +1,3 @@ -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isNil, isUndefined } from '@nestjs/common/utils/shared.utils'; import { throwError as _throw, connectable, @@ -13,12 +10,12 @@ import { KAFKA_DEFAULT_BROKER, KAFKA_DEFAULT_CLIENT, KAFKA_DEFAULT_GROUP, -} from '../constants'; -import { KafkaResponseDeserializer } from '../deserializers/kafka-response.deserializer'; -import { KafkaHeaders } from '../enums'; -import { InvalidKafkaClientTopicException } from '../errors/invalid-kafka-client-topic.exception'; -import { InvalidMessageException } from '../errors/invalid-message.exception'; -import { KafkaStatus } from '../events'; +} from '../constants.js'; +import { KafkaResponseDeserializer } from '../deserializers/kafka-response.deserializer.js'; +import { KafkaHeaders } from '../enums/index.js'; +import { InvalidKafkaClientTopicException } from '../errors/invalid-kafka-client-topic.exception.js'; +import { InvalidMessageException } from '../errors/invalid-message.exception.js'; +import { KafkaStatus } from '../events/index.js'; import { BrokersFunction, Consumer, @@ -30,12 +27,12 @@ import { KafkaMessage, Producer, TopicPartitionOffsetAndMetadata, -} from '../external/kafka.interface'; +} from '../external/kafka.interface.js'; import { KafkaLogger, KafkaParser, KafkaReplyPartitionAssigner, -} from '../helpers'; +} from '../helpers/index.js'; import { ClientKafkaProxy, KafkaOptions, @@ -43,14 +40,14 @@ import { OutgoingEvent, ReadPacket, WritePacket, -} from '../interfaces'; +} from '../interfaces/index.js'; import { KafkaRequest, KafkaRequestSerializer, -} from '../serializers/kafka-request.serializer'; -import { ClientProxy } from './client-proxy'; - -let kafkaPackage: any = {}; +} from '../serializers/kafka-request.serializer.js'; +import { ClientProxy } from './client-proxy.js'; +import { Logger } from '@nestjs/common'; +import { loadPackage, isNil, isUndefined } from '@nestjs/common/internal'; /** * @publicApi @@ -117,11 +114,6 @@ export class ClientKafka this.clientId = (clientOptions.clientId || KAFKA_DEFAULT_CLIENT) + postfixId; this.groupId = (consumerOptions.groupId || KAFKA_DEFAULT_GROUP) + postfixId; - - kafkaPackage = loadPackage('kafkajs', ClientKafka.name, () => - require('kafkajs'), - ); - this.parser = new KafkaParser((options && options.parser) || undefined); this.initializeSerializer(options); @@ -146,53 +138,42 @@ export class ClientKafka if (this.initialized) { return this.initialized.then(() => this._producer!); } - /* eslint-disable-next-line no-async-promise-executor */ - this.initialized = new Promise(async (resolve, reject) => { - try { - this.client = this.createClient(); - if (!this.producerOnlyMode) { - const partitionAssigners = [ - ( - config: ConstructorParameters< - typeof KafkaReplyPartitionAssigner - >[1], - ) => new KafkaReplyPartitionAssigner(this, config), - ]; - - const consumerOptions = Object.assign( - { - partitionAssigners, - }, - this.options.consumer || {}, - { - groupId: this.groupId, - }, - ); - - this._consumer = this.client!.consumer(consumerOptions); - this.registerConsumerEventListeners(); - - // Set member assignments on join and rebalance - this._consumer.on( - this._consumer.events.GROUP_JOIN, - this.setConsumerAssignments.bind(this), - ); - await this._consumer.connect(); - await this.bindTopics(); - } - - this._producer = this.client!.producer(this.options.producer || {}); - this.registerProducerEventListeners(); - await this._producer.connect(); - - resolve(); - } catch (err) { - reject(err); - } - }); + this.initialized = this.initializeClientAndConnections(); return this.initialized.then(() => this._producer!); } + private async initializeClientAndConnections(): Promise { + this.client = await this.createClient(); + if (!this.producerOnlyMode) { + const partitionAssigners = [ + ( + config: ConstructorParameters[1], + ) => new KafkaReplyPartitionAssigner(this, config), + ]; + + const consumerOptions = { + partitionAssigners, + ...(this.options.consumer || {}), + groupId: this.groupId, + }; + + this._consumer = this.client!.consumer(consumerOptions); + this.registerConsumerEventListeners(); + + // Set member assignments on join and rebalance + this._consumer.on( + this._consumer.events.GROUP_JOIN, + this.setConsumerAssignments.bind(this), + ); + await this._consumer.connect(); + await this.bindTopics(); + } + + this._producer = this.client!.producer(this.options.producer || {}); + this.registerProducerEventListeners(); + await this._producer.connect(); + } + public async bindTopics(): Promise { if (!this._consumer) { throw Error('No consumer initialized'); @@ -207,20 +188,26 @@ export class ClientKafka }); } - await this._consumer.run( - Object.assign(this.options.run || {}, { - eachMessage: this.createResponseCallback(), - }), - ); + await this._consumer.run({ + ...(this.options.run || {}), + eachMessage: this.createResponseCallback(), + }); } - public createClient(): T { - const kafkaConfig: KafkaConfig = Object.assign( - { logCreator: KafkaLogger.bind(null, this.logger) }, - this.options.client, - { brokers: this.brokers, clientId: this.clientId }, + public async createClient(): Promise { + const kafkaPackage = await loadPackage( + 'kafkajs', + ClientKafka.name, + () => import('kafkajs'), ); + const kafkaConfig: KafkaConfig = { + logCreator: KafkaLogger.bind(null, this.logger), + ...this.options.client, + brokers: this.brokers, + clientId: this.clientId, + }; + return new kafkaPackage.Kafka(kafkaConfig); } @@ -349,13 +336,11 @@ export class ClientKafka }), ); - const message = Object.assign( - { - topic: pattern, - messages: outgoingEvents, - }, - this.options.send || {}, - ); + const message = { + topic: pattern, + messages: outgoingEvents, + ...(this.options.send || {}), + }; return this.producer.send(message); } @@ -365,13 +350,11 @@ export class ClientKafka const outgoingEvent = await this.serializer.serialize(packet.data, { pattern, }); - const message = Object.assign( - { - topic: pattern, - messages: [outgoingEvent], - }, - this.options.send || {}, - ); + const message = { + topic: pattern, + messages: [outgoingEvent], + ...(this.options.send || {}), + }; return this._producer!.send(message); } @@ -411,13 +394,11 @@ export class ClientKafka serializedPacket.headers[KafkaHeaders.REPLY_PARTITION] = replyPartition; - const message = Object.assign( - { - topic: pattern, - messages: [serializedPacket], - }, - this.options.send || {}, - ); + const message = { + topic: pattern, + messages: [serializedPacket], + ...(this.options.send || {}), + }; return this._producer!.send(message); }) @@ -438,13 +419,13 @@ export class ClientKafka const consumerAssignments: { [key: string]: number } = {}; // Only need to set the minimum - Object.keys(data.payload.memberAssignment).forEach(topic => { - const memberPartitions = data.payload.memberAssignment[topic]; - + for (const [topic, memberPartitions] of Object.entries( + data.payload.memberAssignment, + )) { if (memberPartitions.length) { consumerAssignments[topic] = Math.min(...memberPartitions); } - }); + } this.consumerAssignments = consumerAssignments; } diff --git a/packages/microservices/client/client-mqtt.ts b/packages/microservices/client/client-mqtt.ts index a341544f011..bcd4873ae9c 100644 --- a/packages/microservices/client/client-mqtt.ts +++ b/packages/microservices/client/client-mqtt.ts @@ -1,17 +1,21 @@ -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isObject } from '@nestjs/common/utils/shared.utils'; +import { createRequire } from 'module'; import { EmptyError, fromEvent, lastValueFrom, merge, Observable } from 'rxjs'; import { first, map, share, tap } from 'rxjs/operators'; -import { ECONNREFUSED, ENOTFOUND, MQTT_DEFAULT_URL } from '../constants'; -import { MqttEvents, MqttEventsMap, MqttStatus } from '../events/mqtt.events'; -import { MqttOptions, ReadPacket, WritePacket } from '../interfaces'; +import { ECONNREFUSED, ENOTFOUND, MQTT_DEFAULT_URL } from '../constants.js'; +import { + MqttEvents, + MqttEventsMap, + MqttStatus, +} from '../events/mqtt.events.js'; +import { MqttOptions, ReadPacket, WritePacket } from '../interfaces/index.js'; import { MqttRecord, MqttRecordOptions, -} from '../record-builders/mqtt.record-builder'; -import { MqttRecordSerializer } from '../serializers/mqtt-record.serializer'; -import { ClientProxy } from './client-proxy'; +} from '../record-builders/mqtt.record-builder.js'; +import { MqttRecordSerializer } from '../serializers/mqtt-record.serializer.js'; +import { ClientProxy } from './client-proxy.js'; +import { Logger } from '@nestjs/common'; +import { loadPackageSync, isObject } from '@nestjs/common/internal'; let mqttPackage: any = {}; @@ -43,7 +47,9 @@ export class ClientMqtt extends ClientProxy { super(); this.url = this.getOptionsProp(this.options, 'url') ?? MQTT_DEFAULT_URL; - mqttPackage = loadPackage('mqtt', ClientMqtt.name, () => require('mqtt')); + mqttPackage = loadPackageSync('mqtt', ClientMqtt.name, () => + createRequire(import.meta.url)('mqtt'), + ); this.initializeSerializer(options); this.initializeDeserializer(options); @@ -66,7 +72,7 @@ export class ClientMqtt extends ClientProxy { this.pendingEventListeners = []; } - public connect(): Promise { + public async connect(): Promise { if (this.mqttClient) { return this.connectionPromise!; } diff --git a/packages/microservices/client/client-nats.ts b/packages/microservices/client/client-nats.ts index f64837d44b6..824370febda 100644 --- a/packages/microservices/client/client-nats.ts +++ b/packages/microservices/client/client-nats.ts @@ -1,15 +1,24 @@ -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { EventEmitter } from 'stream'; -import { NATS_DEFAULT_URL } from '../constants'; -import { NatsResponseJSONDeserializer } from '../deserializers/nats-response-json.deserializer'; -import { EmptyResponseException } from '../errors/empty-response.exception'; -import { NatsEvents, NatsEventsMap, NatsStatus } from '../events/nats.events'; -import { NatsOptions, PacketId, ReadPacket, WritePacket } from '../interfaces'; -import { NatsRecord } from '../record-builders'; -import { NatsRecordSerializer } from '../serializers/nats-record.serializer'; -import { ClientProxy } from './client-proxy'; +import { Logger } from '@nestjs/common'; +import { isObject, loadPackageSync } from '@nestjs/common/internal'; +import { EventEmitter } from 'events'; +import { createRequire } from 'module'; +import { NATS_DEFAULT_URL } from '../constants.js'; +import { NatsResponseJSONDeserializer } from '../deserializers/nats-response-json.deserializer.js'; +import { EmptyResponseException } from '../errors/empty-response.exception.js'; +import { + NatsEvents, + NatsEventsMap, + NatsStatus, +} from '../events/nats.events.js'; +import { + NatsOptions, + PacketId, + ReadPacket, + WritePacket, +} from '../interfaces/index.js'; +import { NatsRecord } from '../record-builders/index.js'; +import { NatsRecordSerializer } from '../serializers/nats-record.serializer.js'; +import { ClientProxy } from './client-proxy.js'; let natsPackage = {} as any; @@ -17,8 +26,8 @@ let natsPackage = {} as any; // because it would require the user to install the nats package even if they dont use Nats // Otherwise, TypeScript would fail to compile the code. // -// type Client = import('nats').NatsConnection; -// type NatsMsg = import('nats').Msg; +// type Client = import('@nats-io/transport-node').NatsConnection; +// type NatsMsg = import('@nats-io/transport-node').Msg; type Client = Record; type NatsMsg = Record; @@ -37,7 +46,11 @@ export class ClientNats extends ClientProxy { constructor(protected readonly options: Required['options']) { super(); - natsPackage = loadPackage('nats', ClientNats.name, () => require('nats')); + natsPackage = loadPackageSync( + '@nats-io/transport-node', + ClientNats.name, + () => createRequire(import.meta.url)('@nats-io/transport-node'), + ); this.initializeSerializer(options); this.initializeDeserializer(options); @@ -66,7 +79,21 @@ export class ClientNats extends ClientProxy { return this.natsClient; } - public createClient(): Promise { + public async createClient(): Promise { + // Eagerly initialize serializer/deserializer so they can be used synchronously + if ( + this.serializer && + typeof (this.serializer as any).init === 'function' + ) { + await (this.serializer as any).init(); + } + if ( + this.deserializer && + typeof (this.deserializer as any).init === 'function' + ) { + await (this.deserializer as any).init(); + } + const options = this.options || ({} as NatsOptions); return natsPackage.connect({ servers: NATS_DEFAULT_URL, @@ -76,15 +103,10 @@ export class ClientNats extends ClientProxy { public async handleStatusUpdates(client: Client) { for await (const status of client.status()) { - const data = - status.data && isObject(status.data) - ? JSON.stringify(status.data) - : status.data; - switch (status.type) { case 'error': this.logger.error( - `NatsError: type: "${status.type}", data: "${data}".`, + `NatsError: type: "${status.type}", error: "${status.error}".`, ); break; @@ -95,14 +117,12 @@ export class ClientNats extends ClientProxy { // Prevent unhandled promise rejection this.connectionPromise.catch(() => {}); - this.logger.error( - `NatsError: type: "${status.type}", data: "${data}".`, - ); + this.logger.error(`NatsError: type: "${status.type}".`); this._status$.next(NatsStatus.DISCONNECTED); this.statusEventEmitter.emit( NatsEventsMap.DISCONNECT, - status.data as string, + status.server as string, ); break; @@ -112,37 +132,42 @@ export class ClientNats extends ClientProxy { case 'reconnect': this.connectionPromise = Promise.resolve(client); - this.logger.log( - `NatsStatus: type: "${status.type}", data: "${data}".`, - ); + this.logger.log(`NatsStatus: type: "${status.type}".`); this._status$.next(NatsStatus.CONNECTED); this.statusEventEmitter.emit( NatsEventsMap.RECONNECT, - status.data as string, + status.server as string, ); break; - case 'pingTimer': + case 'ping': if (this.options.debug) { this.logger.debug( - `NatsStatus: type: "${status.type}", data: "${data}".`, + `NatsStatus: type: "${status.type}", pending pings: "${status.pendingPings}".`, ); } break; case 'update': this.logger.log( - `NatsStatus: type: "${status.type}", data: "${data}".`, + `NatsStatus: type: "${status.type}", added: "${status.added}", deleted: "${status.deleted}".`, ); - this.statusEventEmitter.emit(NatsEventsMap.UPDATE, status.data); + this.statusEventEmitter.emit(NatsEventsMap.UPDATE, undefined); break; - default: + default: { + const data = + 'data' in status && isObject(status.data) + ? JSON.stringify(status.data) + : 'data' in status + ? status.data + : ''; this.logger.log( `NatsStatus: type: "${status.type}", data: "${data}".`, ); break; + } } } } @@ -167,7 +192,7 @@ export class ClientNats extends ClientProxy { packet: ReadPacket & PacketId, callback: (packet: WritePacket) => any, ) { - return async (error: string | Error | undefined, natsMsg: NatsMsg) => { + return async (error: Error | null, natsMsg: NatsMsg) => { if (error) { return callback({ err: error, @@ -182,7 +207,7 @@ export class ClientNats extends ClientProxy { isDisposed: true, }); } - const message = await this.deserializer.deserialize(rawPacket); + const message = await this.deserializer.deserialize(natsMsg); if (message.id && message.id !== packet.id) { return undefined; } @@ -208,7 +233,9 @@ export class ClientNats extends ClientProxy { try { const packet = this.assignPacketId(partialPacket); const channel = this.normalizePattern(partialPacket.pattern); - const serializedPacket: NatsRecord = this.serializer.serialize(packet); + const serializedPacket: NatsRecord = this.serializer.serialize( + packet, + ) as NatsRecord; const inbox = natsPackage.createInbox(this.options.inboxPrefix); const subscriptionHandler = this.createSubscriptionHandler( @@ -217,7 +244,10 @@ export class ClientNats extends ClientProxy { ); const subscription = this.natsClient!.subscribe(inbox, { - callback: subscriptionHandler, + callback: subscriptionHandler as ( + err: Error | null, + msg: NatsMsg, + ) => Promise, }); const headers = this.mergeHeaders(serializedPacket.headers); @@ -233,9 +263,10 @@ export class ClientNats extends ClientProxy { } } - protected dispatchEvent(packet: ReadPacket): Promise { + protected async dispatchEvent(packet: ReadPacket): Promise { const pattern = this.normalizePattern(packet.pattern); - const serializedPacket: NatsRecord = this.serializer.serialize(packet); + const serializedPacket: NatsRecord = + await this.serializer.serialize(packet); const headers = this.mergeHeaders(serializedPacket.headers); return new Promise((resolve, reject) => { diff --git a/packages/microservices/client/client-proxy-factory.ts b/packages/microservices/client/client-proxy-factory.ts index ab7abccb033..6fca853135d 100644 --- a/packages/microservices/client/client-proxy-factory.ts +++ b/packages/microservices/client/client-proxy-factory.ts @@ -1,10 +1,10 @@ -import { Transport } from '../enums/transport.enum'; -import { ClientKafkaProxy } from '../interfaces'; +import { Transport } from '../enums/transport.enum.js'; +import { ClientKafkaProxy } from '../interfaces/index.js'; import { ClientOptions, CustomClientOptions, TcpClientOptions, -} from '../interfaces/client-metadata.interface'; +} from '../interfaces/client-metadata.interface.js'; import { GrpcOptions, KafkaOptions, @@ -12,15 +12,15 @@ import { NatsOptions, RedisOptions, RmqOptions, -} from '../interfaces/microservice-configuration.interface'; -import { ClientGrpcProxy } from './client-grpc'; -import { ClientKafka } from './client-kafka'; -import { ClientMqtt } from './client-mqtt'; -import { ClientNats } from './client-nats'; -import { ClientProxy } from './client-proxy'; -import { ClientRedis } from './client-redis'; -import { ClientRMQ } from './client-rmq'; -import { ClientTCP } from './client-tcp'; +} from '../interfaces/microservice-configuration.interface.js'; +import { ClientGrpcProxy } from './client-grpc.js'; +import { ClientKafka } from './client-kafka.js'; +import { ClientMqtt } from './client-mqtt.js'; +import { ClientNats } from './client-nats.js'; +import { ClientProxy } from './client-proxy.js'; +import { ClientRedis } from './client-redis.js'; +import { ClientRMQ } from './client-rmq.js'; +import { ClientTCP } from './client-tcp.js'; export interface IClientProxyFactory { create(clientOptions: ClientOptions): ClientProxy; diff --git a/packages/microservices/client/client-proxy.ts b/packages/microservices/client/client-proxy.ts index e2d12463e54..f15bb473f92 100644 --- a/packages/microservices/client/client-proxy.ts +++ b/packages/microservices/client/client-proxy.ts @@ -1,5 +1,3 @@ -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; -import { isNil } from '@nestjs/common/utils/shared.utils'; import { throwError as _throw, connectable, @@ -12,8 +10,8 @@ import { Subject, } from 'rxjs'; import { distinctUntilChanged, map, mergeMap, take } from 'rxjs/operators'; -import { IncomingResponseDeserializer } from '../deserializers/incoming-response.deserializer'; -import { InvalidMessageException } from '../errors/invalid-message.exception'; +import { IncomingResponseDeserializer } from '../deserializers/incoming-response.deserializer.js'; +import { InvalidMessageException } from '../errors/invalid-message.exception.js'; import { ClientOptions, KafkaOptions, @@ -26,11 +24,12 @@ import { RmqOptions, TcpClientOptions, WritePacket, -} from '../interfaces'; -import { ProducerDeserializer } from '../interfaces/deserializer.interface'; -import { ProducerSerializer } from '../interfaces/serializer.interface'; -import { IdentitySerializer } from '../serializers/identity.serializer'; -import { transformPatternToRoute } from '../utils'; +} from '../interfaces/index.js'; +import { ProducerDeserializer } from '../interfaces/deserializer.interface.js'; +import { ProducerSerializer } from '../interfaces/serializer.interface.js'; +import { IdentitySerializer } from '../serializers/identity.serializer.js'; +import { transformPatternToRoute } from '../utils/index.js'; +import { randomStringGenerator, isNil } from '@nestjs/common/internal'; /** * @publicApi diff --git a/packages/microservices/client/client-redis.ts b/packages/microservices/client/client-redis.ts index 9c380400fa5..d7f0f635c09 100644 --- a/packages/microservices/client/client-redis.ts +++ b/packages/microservices/client/client-redis.ts @@ -1,13 +1,13 @@ -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { REDIS_DEFAULT_HOST, REDIS_DEFAULT_PORT } from '../constants'; +import { REDIS_DEFAULT_HOST, REDIS_DEFAULT_PORT } from '../constants.js'; import { RedisEvents, RedisEventsMap, RedisStatus, -} from '../events/redis.events'; -import { ReadPacket, RedisOptions, WritePacket } from '../interfaces'; -import { ClientProxy } from './client-proxy'; +} from '../events/redis.events.js'; +import { ReadPacket, RedisOptions, WritePacket } from '../interfaces/index.js'; +import { ClientProxy } from './client-proxy.js'; +import { Logger } from '@nestjs/common'; +import { loadPackage } from '@nestjs/common/internal'; // To enable type safety for Redis. This cant be uncommented by default // because it would require the user to install the ioredis package even if they dont use Redis @@ -16,8 +16,6 @@ import { ClientProxy } from './client-proxy'; // type Redis = import('ioredis').Redis; type Redis = any; -let redisPackage = {} as any; - /** * @publicApi */ @@ -26,7 +24,7 @@ export class ClientRedis extends ClientProxy { protected readonly subscriptionsCount = new Map(); protected pubClient: Redis; protected subClient: Redis; - protected connectionPromise: Promise; + protected connectionPromise: Promise | null = null; protected isManuallyClosed = false; protected wasInitialConnectionSuccessful = false; protected pendingEventListeners: Array<{ @@ -37,10 +35,6 @@ export class ClientRedis extends ClientProxy { constructor(protected readonly options: Required['options']) { super(); - redisPackage = loadPackage('ioredis', ClientRedis.name, () => - require('ioredis'), - ); - this.initializeSerializer(options); this.initializeDeserializer(options); } @@ -58,15 +52,21 @@ export class ClientRedis extends ClientProxy { this.pubClient && (await this.pubClient.quit()); this.subClient && (await this.subClient.quit()); this.pubClient = this.subClient = null; + this.connectionPromise = null; this.pendingEventListeners = []; } - public async connect(): Promise { - if (this.pubClient && this.subClient) { + public connect(): Promise { + if (this.connectionPromise) { return this.connectionPromise; } - this.pubClient = this.createClient(); - this.subClient = this.createClient(); + this.connectionPromise = this.handleConnection(); + return this.connectionPromise; + } + + private async handleConnection(): Promise { + this.pubClient = await this.createClient(); + this.subClient = await this.createClient(); [this.pubClient, this.subClient].forEach((client, index) => { const type = index === 0 ? 'pub' : 'sub'; @@ -80,16 +80,17 @@ export class ClientRedis extends ClientProxy { }); this.pendingEventListeners = []; - this.connectionPromise = Promise.all([ - this.subClient.connect(), - this.pubClient.connect(), - ]); - await this.connectionPromise; - return this.connectionPromise; + await Promise.all([this.subClient.connect(), this.pubClient.connect()]); } - public createClient(): Redis { - return new redisPackage({ + public async createClient(): Promise { + const redisPackage = await loadPackage( + 'ioredis', + ClientRedis.name, + () => import('ioredis'), + ); + const RedisClient = redisPackage.default || redisPackage; + return new RedisClient({ host: REDIS_DEFAULT_HOST, port: REDIS_DEFAULT_PORT, ...this.getClientOptions(), diff --git a/packages/microservices/client/client-rmq.ts b/packages/microservices/client/client-rmq.ts index b62602802c2..d58a4cd2da9 100644 --- a/packages/microservices/client/client-rmq.ts +++ b/packages/microservices/client/client-rmq.ts @@ -1,8 +1,5 @@ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */ -import { Logger } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; -import { isFunction, isString } from '@nestjs/common/utils/shared.utils'; +import { createRequire } from 'module'; import { EventEmitter } from 'events'; import { EmptyError, @@ -15,20 +12,27 @@ import { import { first, map, retryWhen, scan, skip, switchMap } from 'rxjs/operators'; import { DISCONNECTED_RMQ_MESSAGE, - RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT, - RQM_DEFAULT_NO_ASSERT, - RQM_DEFAULT_NOACK, - RQM_DEFAULT_PERSISTENT, - RQM_DEFAULT_PREFETCH_COUNT, - RQM_DEFAULT_QUEUE, - RQM_DEFAULT_QUEUE_OPTIONS, - RQM_DEFAULT_URL, -} from '../constants'; -import { RmqEvents, RmqEventsMap, RmqStatus } from '../events/rmq.events'; -import { ReadPacket, RmqOptions, WritePacket } from '../interfaces'; -import { RmqRecord } from '../record-builders'; -import { RmqRecordSerializer } from '../serializers/rmq-record.serializer'; -import { ClientProxy } from './client-proxy'; + RMQ_DEFAULT_IS_GLOBAL_PREFETCH_COUNT, + RMQ_DEFAULT_NO_ASSERT, + RMQ_DEFAULT_NOACK, + RMQ_DEFAULT_PERSISTENT, + RMQ_DEFAULT_PREFETCH_COUNT, + RMQ_DEFAULT_QUEUE, + RMQ_DEFAULT_QUEUE_OPTIONS, + RMQ_DEFAULT_URL, +} from '../constants.js'; +import { RmqEvents, RmqEventsMap, RmqStatus } from '../events/rmq.events.js'; +import { ReadPacket, RmqOptions, WritePacket } from '../interfaces/index.js'; +import { RmqRecord } from '../record-builders/index.js'; +import { RmqRecordSerializer } from '../serializers/rmq-record.serializer.js'; +import { ClientProxy } from './client-proxy.js'; +import { Logger } from '@nestjs/common'; +import { + loadPackageSync, + randomStringGenerator, + isFunction, + isString, +} from '@nestjs/common/internal'; // To enable type safety for RMQ. This cant be uncommented by default // because it would require the user to install the amqplib package even if they dont use RabbitMQ @@ -71,11 +75,11 @@ export class ClientRMQ extends ClientProxy { constructor(protected readonly options: Required['options']) { super(); - this.queue = this.getOptionsProp(this.options, 'queue', RQM_DEFAULT_QUEUE); + this.queue = this.getOptionsProp(this.options, 'queue', RMQ_DEFAULT_QUEUE); this.queueOptions = this.getOptionsProp( this.options, 'queueOptions', - RQM_DEFAULT_QUEUE_OPTIONS, + RMQ_DEFAULT_QUEUE_OPTIONS, ); this.replyQueue = this.getOptionsProp( this.options, @@ -85,11 +89,15 @@ export class ClientRMQ extends ClientProxy { this.noAssert = this.getOptionsProp(this.options, 'noAssert') ?? this.queueOptions.noAssert ?? - RQM_DEFAULT_NO_ASSERT; + RMQ_DEFAULT_NO_ASSERT; - loadPackage('amqplib', ClientRMQ.name, () => require('amqplib')); - rmqPackage = loadPackage('amqp-connection-manager', ClientRMQ.name, () => - require('amqp-connection-manager'), + loadPackageSync('amqplib', ClientRMQ.name, () => + createRequire(import.meta.url)('amqplib'), + ); + rmqPackage = loadPackageSync( + 'amqp-connection-manager', + ClientRMQ.name, + () => createRequire(import.meta.url)('amqp-connection-manager'), ); this.initializeSerializer(options); @@ -104,7 +112,7 @@ export class ClientRMQ extends ClientProxy { this.pendingEventListeners = []; } - public connect(): Promise { + public async connect(): Promise { if (this.client) { return this.connectionPromise; } @@ -150,7 +158,7 @@ export class ClientRMQ extends ClientProxy { public createClient(): AmqpConnectionManager { const socketOptions = this.getOptionsProp(this.options, 'socketOptions'); - const urls = this.getOptionsProp(this.options, 'urls') || [RQM_DEFAULT_URL]; + const urls = this.getOptionsProp(this.options, 'urls') || [RMQ_DEFAULT_URL]; return rmqPackage.connect(urls, socketOptions); } @@ -199,10 +207,10 @@ export class ClientRMQ extends ClientProxy { public async setupChannel(channel: Channel, resolve: Function) { const prefetchCount = this.getOptionsProp(this.options, 'prefetchCount') || - RQM_DEFAULT_PREFETCH_COUNT; + RMQ_DEFAULT_PREFETCH_COUNT; const isGlobalPrefetchCount = this.getOptionsProp(this.options, 'isGlobalPrefetchCount') || - RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT; + RMQ_DEFAULT_IS_GLOBAL_PREFETCH_COUNT; if (!this.options.wildcards && this.options.exchangeType !== 'fanout') { if (!this.noAssert) { @@ -239,7 +247,7 @@ export class ClientRMQ extends ClientProxy { } public async consumeChannel(channel: Channel) { - const noAck = this.getOptionsProp(this.options, 'noAck', RQM_DEFAULT_NOACK); + const noAck = this.getOptionsProp(this.options, 'noAck', RMQ_DEFAULT_NOACK); await channel.consume( this.replyQueue, (msg: ConsumeMessage | null) => @@ -384,7 +392,7 @@ export class ClientRMQ extends ClientProxy { persistent: this.getOptionsProp( this.options, 'persistent', - RQM_DEFAULT_PERSISTENT, + RMQ_DEFAULT_PERSISTENT, ), ...options, headers: this.mergeHeaders(options?.headers), @@ -435,7 +443,7 @@ export class ClientRMQ extends ClientProxy { persistent: this.getOptionsProp( this.options, 'persistent', - RQM_DEFAULT_PERSISTENT, + RMQ_DEFAULT_PERSISTENT, ), ...options, headers: this.mergeHeaders(options?.headers), diff --git a/packages/microservices/client/client-tcp.ts b/packages/microservices/client/client-tcp.ts index 098bc0d6a98..9e6d84113b7 100644 --- a/packages/microservices/client/client-tcp.ts +++ b/packages/microservices/client/client-tcp.ts @@ -1,14 +1,18 @@ -import { Logger, Type } from '@nestjs/common'; +import { Logger, type Type } from '@nestjs/common'; import * as net from 'net'; import { EmptyError, lastValueFrom } from 'rxjs'; import { share, tap } from 'rxjs/operators'; import { ConnectionOptions, connect as tlsConnect, TLSSocket } from 'tls'; -import { ECONNREFUSED, TCP_DEFAULT_HOST, TCP_DEFAULT_PORT } from '../constants'; -import { TcpEvents, TcpEventsMap, TcpStatus } from '../events/tcp.events'; -import { JsonSocket, TcpSocket } from '../helpers'; -import { PacketId, ReadPacket, WritePacket } from '../interfaces'; -import { TcpClientOptions } from '../interfaces/client-metadata.interface'; -import { ClientProxy } from './client-proxy'; +import { + ECONNREFUSED, + TCP_DEFAULT_HOST, + TCP_DEFAULT_PORT, +} from '../constants.js'; +import { TcpEvents, TcpEventsMap, TcpStatus } from '../events/tcp.events.js'; +import { JsonSocket, TcpSocket } from '../helpers/index.js'; +import { PacketId, ReadPacket, WritePacket } from '../interfaces/index.js'; +import { TcpClientOptions } from '../interfaces/client-metadata.interface.js'; +import { ClientProxy } from './client-proxy.js'; /** * @publicApi diff --git a/packages/microservices/client/index.ts b/packages/microservices/client/index.ts index c75c4849d0d..1c07e000ef4 100644 --- a/packages/microservices/client/index.ts +++ b/packages/microservices/client/index.ts @@ -1,9 +1,9 @@ -export * from './client-grpc'; -export * from './client-kafka'; -export * from './client-mqtt'; -export * from './client-nats'; -export * from './client-proxy'; -export { ClientProxyFactory } from './client-proxy-factory'; -export * from './client-redis'; -export * from './client-rmq'; -export * from './client-tcp'; +export * from './client-grpc.js'; +export * from './client-kafka.js'; +export * from './client-mqtt.js'; +export * from './client-nats.js'; +export * from './client-proxy.js'; +export { ClientProxyFactory } from './client-proxy-factory.js'; +export * from './client-redis.js'; +export * from './client-rmq.js'; +export * from './client-tcp.js'; diff --git a/packages/microservices/constants.ts b/packages/microservices/constants.ts index dc4ec1cad4d..d40d6a7e4d7 100644 --- a/packages/microservices/constants.ts +++ b/packages/microservices/constants.ts @@ -1,4 +1,4 @@ -import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants'; +import { ROUTE_ARGS_METADATA } from '@nestjs/common/internal'; export const TCP_DEFAULT_PORT = 3000; export const TCP_DEFAULT_HOST = 'localhost'; @@ -7,20 +7,20 @@ export const REDIS_DEFAULT_HOST = 'localhost'; export const NATS_DEFAULT_URL = 'nats://localhost:4222'; export const MQTT_DEFAULT_URL = 'mqtt://localhost:1883'; export const GRPC_DEFAULT_URL = 'localhost:5000'; -export const RQM_DEFAULT_URL = 'amqp://localhost'; +export const RMQ_DEFAULT_URL = 'amqp://localhost'; export const KAFKA_DEFAULT_BROKER = 'localhost:9092'; export const KAFKA_DEFAULT_CLIENT = 'nestjs-consumer'; export const KAFKA_DEFAULT_GROUP = 'nestjs-group'; export const MQTT_SEPARATOR = '/'; export const MQTT_WILDCARD_SINGLE = '+'; export const MQTT_WILDCARD_ALL = '#'; -export const RQM_DEFAULT_QUEUE = ''; -export const RQM_DEFAULT_PREFETCH_COUNT = 0; -export const RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT = false; -export const RQM_DEFAULT_QUEUE_OPTIONS = {}; -export const RQM_DEFAULT_NOACK = true; -export const RQM_DEFAULT_PERSISTENT = false; -export const RQM_DEFAULT_NO_ASSERT = false; +export const RMQ_DEFAULT_QUEUE = ''; +export const RMQ_DEFAULT_PREFETCH_COUNT = 0; +export const RMQ_DEFAULT_IS_GLOBAL_PREFETCH_COUNT = false; +export const RMQ_DEFAULT_QUEUE_OPTIONS = {}; +export const RMQ_DEFAULT_NOACK = true; +export const RMQ_DEFAULT_PERSISTENT = false; +export const RMQ_DEFAULT_NO_ASSERT = false; export const RMQ_SEPARATOR = '.'; export const RMQ_WILDCARD_SINGLE = '*'; export const RMQ_WILDCARD_ALL = '#'; @@ -40,12 +40,12 @@ export const PARAM_ARGS_METADATA = ROUTE_ARGS_METADATA; export const REQUEST_PATTERN_METADATA = 'microservices:request_pattern'; export const REPLY_PATTERN_METADATA = 'microservices:reply_pattern'; -export const RQM_NO_EVENT_HANDLER = ( +export const RMQ_NO_EVENT_HANDLER = ( text: TemplateStringsArray, pattern: string, ) => `An unsupported event was received. It has been negative acknowledged, so it will not be re-delivered. Pattern: ${pattern}`; -export const RQM_NO_MESSAGE_HANDLER = ( +export const RMQ_NO_MESSAGE_HANDLER = ( text: TemplateStringsArray, pattern: string, ) => diff --git a/packages/microservices/container.ts b/packages/microservices/container.ts index 87812833d94..f0b9e44214c 100644 --- a/packages/microservices/container.ts +++ b/packages/microservices/container.ts @@ -1,4 +1,4 @@ -import { ClientProxy } from './client/client-proxy'; +import { ClientProxy } from './client/client-proxy.js'; export class ClientsContainer { private clients: ClientProxy[] = []; diff --git a/packages/microservices/context/exception-filters-context.ts b/packages/microservices/context/exception-filters-context.ts index 14d42da915e..6f07879b629 100644 --- a/packages/microservices/context/exception-filters-context.ts +++ b/packages/microservices/context/exception-filters-context.ts @@ -1,14 +1,17 @@ -import { EXCEPTION_FILTERS_METADATA } from '@nestjs/common/constants'; -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { BaseExceptionFilterContext } from '@nestjs/core/exceptions/base-exception-filter-context'; -import { STATIC_CONTEXT } from '@nestjs/core/injector/constants'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; import { iterate } from 'iterare'; import { Observable } from 'rxjs'; -import { RpcExceptionsHandler } from '../exceptions/rpc-exceptions-handler'; +import { RpcExceptionsHandler } from '../exceptions/rpc-exceptions-handler.js'; +import { + EXCEPTION_FILTERS_METADATA, + type Controller, + isEmptyArray, +} from '@nestjs/common/internal'; +import type { ApplicationConfig, NestContainer } from '@nestjs/core'; +import { + BaseExceptionFilterContext, + STATIC_CONTEXT, + type InstanceWrapper, +} from '@nestjs/core/internal'; /** * @publicApi @@ -38,7 +41,7 @@ export class ExceptionFiltersContext extends BaseExceptionFilterContext { contextId, inquirerId, ); - if (isEmpty(filters)) { + if (isEmptyArray(filters)) { return exceptionHandler; } exceptionHandler.setCustomFilters(filters.reverse()); diff --git a/packages/microservices/context/request-context-host.ts b/packages/microservices/context/request-context-host.ts index 890106fd9b5..26537f8f90f 100644 --- a/packages/microservices/context/request-context-host.ts +++ b/packages/microservices/context/request-context-host.ts @@ -1,5 +1,5 @@ -import { BaseRpcContext } from '../ctx-host/base-rpc.context'; -import { RequestContext } from '../interfaces'; +import { BaseRpcContext } from '../ctx-host/base-rpc.context.js'; +import { RequestContext } from '../interfaces/index.js'; /** * @publicApi diff --git a/packages/microservices/context/rpc-context-creator.ts b/packages/microservices/context/rpc-context-creator.ts index a7ee74e025a..1399b31c03d 100644 --- a/packages/microservices/context/rpc-context-creator.ts +++ b/packages/microservices/context/rpc-context-creator.ts @@ -1,35 +1,37 @@ +import type { + ContextType, + PipeTransform, + PreRequestHook, +} from '@nestjs/common'; import { + type Controller, CUSTOM_ROUTE_ARGS_METADATA, + isEmptyArray, PARAMTYPES_METADATA, -} from '@nestjs/common/constants'; -import { - ContextType, - Controller, - PipeTransform, -} from '@nestjs/common/interfaces'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { FORBIDDEN_MESSAGE } from '@nestjs/core/guards/constants'; -import { GuardsConsumer } from '@nestjs/core/guards/guards-consumer'; -import { GuardsContextCreator } from '@nestjs/core/guards/guards-context-creator'; +} from '@nestjs/common/internal'; +import type { ApplicationConfig } from '@nestjs/core'; import { ContextUtils, - ParamProperties, -} from '@nestjs/core/helpers/context-utils'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { HandlerMetadataStorage } from '@nestjs/core/helpers/handler-metadata-storage'; -import { ParamsMetadata } from '@nestjs/core/helpers/interfaces'; -import { STATIC_CONTEXT } from '@nestjs/core/injector/constants'; -import { InterceptorsConsumer } from '@nestjs/core/interceptors/interceptors-consumer'; -import { InterceptorsContextCreator } from '@nestjs/core/interceptors/interceptors-context-creator'; -import { PipesConsumer } from '@nestjs/core/pipes/pipes-consumer'; -import { PipesContextCreator } from '@nestjs/core/pipes/pipes-context-creator'; -import { Observable } from 'rxjs'; -import { PARAM_ARGS_METADATA } from '../constants'; -import { RpcException } from '../exceptions'; -import { RpcParamsFactory } from '../factories/rpc-params-factory'; -import { ExceptionFiltersContext } from './exception-filters-context'; -import { DEFAULT_CALLBACK_METADATA } from './rpc-metadata-constants'; -import { RpcProxy } from './rpc-proxy'; + ExecutionContextHost, + FORBIDDEN_MESSAGE, + type GuardsConsumer, + type GuardsContextCreator, + HandlerMetadataStorage, + type InterceptorsConsumer, + type InterceptorsContextCreator, + type ParamProperties, + type ParamsMetadata, + type PipesConsumer, + type PipesContextCreator, + STATIC_CONTEXT, +} from '@nestjs/core/internal'; +import { defer, from, mergeMap, Observable } from 'rxjs'; +import { PARAM_ARGS_METADATA } from '../constants.js'; +import { RpcException } from '../exceptions/index.js'; +import { RpcParamsFactory } from '../factories/rpc-params-factory.js'; +import { ExceptionFiltersContext } from './exception-filters-context.js'; +import { DEFAULT_CALLBACK_METADATA } from './rpc-metadata-constants.js'; +import { RpcProxy } from './rpc-proxy.js'; type RpcParamProperties = ParamProperties & { metatype?: any }; export interface RpcHandlerMetadata { @@ -53,6 +55,7 @@ export class RpcContextCreator { private readonly guardsConsumer: GuardsConsumer, private readonly interceptorsContextCreator: InterceptorsContextCreator, private readonly interceptorsConsumer: InterceptorsConsumer, + private readonly applicationConfig?: ApplicationConfig, ) {} public create( @@ -122,18 +125,46 @@ export class RpcContextCreator { return callback.apply(instance, args); }; + const preRequestHooks = + this.applicationConfig?.getGlobalPreRequestHooks() ?? []; + return this.rpcProxy.create(async (...args: unknown[]) => { const initialArgs = this.contextUtils.createNullArray(argsLength); - fnCanActivate && (await fnCanActivate(args)); - return this.interceptorsConsumer.intercept( - interceptors, + const executePipeline = async () => { + fnCanActivate && (await fnCanActivate(args)); + return this.interceptorsConsumer.intercept( + interceptors, + args, + instance, + callback, + handler(initialArgs, args), + contextType, + ) as Promise>; + }; + + if (preRequestHooks.length === 0) { + return executePipeline(); + } + + const executionContext = new ExecutionContextHost( args, - instance, + instance.constructor as any, callback, - handler(initialArgs, args), - contextType, - ) as Promise>; + ); + executionContext.setType(contextType); + + const pipelineObs: Observable = defer(() => + from(executePipeline()).pipe(mergeMap(obs => obs)), + ); + + let index = 0; + const next = (): Observable => { + if (index >= preRequestHooks.length) return pipelineObs; + return preRequestHooks[index++](executionContext, next); + }; + + return next(); }, exceptionHandler); } @@ -220,7 +251,7 @@ export class RpcContextCreator { this.pipesContextCreator.setModuleContext(moduleContext); return keys.map(key => { - const { index, data, pipes: pipesCollection } = metadata[key]; + const { index, data, pipes: pipesCollection, schema } = metadata[key]; const pipes = this.pipesContextCreator.createConcreteContext(pipesCollection); const type = this.contextUtils.mapParamType(key); @@ -232,13 +263,20 @@ export class RpcContextCreator { data, contextFactory, ); - return { index, extractValue: customExtractValue, type, data, pipes }; + return { + index, + extractValue: customExtractValue, + type, + data, + pipes, + schema, + }; } const numericType = Number(type); const extractValue = (...args: unknown[]) => paramsFactory.exchangeKeyForValue(numericType, data, args); - return { index, extractValue, type: numericType, data, pipes }; + return { index, extractValue, type: numericType, data, pipes, schema }; }); } @@ -276,7 +314,7 @@ export class RpcContextCreator { { metatype, type, data }: { metatype: any; type: any; data: any }, pipes: PipeTransform[], ): Promise { - return isEmpty(pipes) + return isEmptyArray(pipes) ? value : this.pipesConsumer.apply(value, { metatype, type, data }, pipes); } diff --git a/packages/microservices/context/rpc-metadata-constants.ts b/packages/microservices/context/rpc-metadata-constants.ts index 9c6d0ef6e82..bcf423f64e4 100644 --- a/packages/microservices/context/rpc-metadata-constants.ts +++ b/packages/microservices/context/rpc-metadata-constants.ts @@ -1,4 +1,4 @@ -import { RpcParamtype } from '../enums/rpc-paramtype.enum'; +import { RpcParamtype } from '../enums/rpc-paramtype.enum.js'; export const DEFAULT_CALLBACK_METADATA = { [`${RpcParamtype.PAYLOAD}:0`]: { index: 0, data: undefined, pipes: [] }, diff --git a/packages/microservices/context/rpc-proxy.ts b/packages/microservices/context/rpc-proxy.ts index 0e90b8e83f1..41b05137d86 100644 --- a/packages/microservices/context/rpc-proxy.ts +++ b/packages/microservices/context/rpc-proxy.ts @@ -1,7 +1,7 @@ -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; import { isObservable, Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { RpcExceptionsHandler } from '../exceptions/rpc-exceptions-handler'; +import { RpcExceptionsHandler } from '../exceptions/rpc-exceptions-handler.js'; +import { ExecutionContextHost } from '@nestjs/core/internal'; export class RpcProxy { public create( diff --git a/packages/microservices/ctx-host/index.ts b/packages/microservices/ctx-host/index.ts index 9d871dee247..262f9939545 100644 --- a/packages/microservices/ctx-host/index.ts +++ b/packages/microservices/ctx-host/index.ts @@ -1,7 +1,7 @@ -export * from './kafka.context'; -export * from './mqtt.context'; -export * from './nats.context'; -export * from './redis.context'; -export * from './rmq.context'; -export * from './tcp.context'; -export * from './base-rpc.context'; +export * from './kafka.context.js'; +export * from './mqtt.context.js'; +export * from './nats.context.js'; +export * from './redis.context.js'; +export * from './rmq.context.js'; +export * from './tcp.context.js'; +export * from './base-rpc.context.js'; diff --git a/packages/microservices/ctx-host/kafka.context.ts b/packages/microservices/ctx-host/kafka.context.ts index 5361ae53de0..0a009747635 100644 --- a/packages/microservices/ctx-host/kafka.context.ts +++ b/packages/microservices/ctx-host/kafka.context.ts @@ -1,5 +1,9 @@ -import { Consumer, KafkaMessage, Producer } from '../external/kafka.interface'; -import { BaseRpcContext } from './base-rpc.context'; +import { + Consumer, + KafkaMessage, + Producer, +} from '../external/kafka.interface.js'; +import { BaseRpcContext } from './base-rpc.context.js'; type KafkaContextArgs = [ message: KafkaMessage, diff --git a/packages/microservices/ctx-host/mqtt.context.ts b/packages/microservices/ctx-host/mqtt.context.ts index e9919b11caf..f091421114e 100644 --- a/packages/microservices/ctx-host/mqtt.context.ts +++ b/packages/microservices/ctx-host/mqtt.context.ts @@ -1,4 +1,4 @@ -import { BaseRpcContext } from './base-rpc.context'; +import { BaseRpcContext } from './base-rpc.context.js'; type MqttContextArgs = [string, Record]; diff --git a/packages/microservices/ctx-host/nats.context.ts b/packages/microservices/ctx-host/nats.context.ts index dc3fe92d861..8ddaa0853f7 100644 --- a/packages/microservices/ctx-host/nats.context.ts +++ b/packages/microservices/ctx-host/nats.context.ts @@ -1,4 +1,4 @@ -import { BaseRpcContext } from './base-rpc.context'; +import { BaseRpcContext } from './base-rpc.context.js'; type NatsContextArgs = [string, any]; diff --git a/packages/microservices/ctx-host/redis.context.ts b/packages/microservices/ctx-host/redis.context.ts index ad5fa7e3f5f..266b7157bd5 100644 --- a/packages/microservices/ctx-host/redis.context.ts +++ b/packages/microservices/ctx-host/redis.context.ts @@ -1,4 +1,4 @@ -import { BaseRpcContext } from './base-rpc.context'; +import { BaseRpcContext } from './base-rpc.context.js'; type RedisContextArgs = [string]; diff --git a/packages/microservices/ctx-host/rmq.context.ts b/packages/microservices/ctx-host/rmq.context.ts index da791a64fb9..a5c00788a64 100644 --- a/packages/microservices/ctx-host/rmq.context.ts +++ b/packages/microservices/ctx-host/rmq.context.ts @@ -1,4 +1,4 @@ -import { BaseRpcContext } from './base-rpc.context'; +import { BaseRpcContext } from './base-rpc.context.js'; type RmqContextArgs = [Record, any, string]; diff --git a/packages/microservices/ctx-host/tcp.context.ts b/packages/microservices/ctx-host/tcp.context.ts index 2b74dc89919..771b69a879e 100644 --- a/packages/microservices/ctx-host/tcp.context.ts +++ b/packages/microservices/ctx-host/tcp.context.ts @@ -1,5 +1,5 @@ -import { TcpSocket } from '../helpers'; -import { BaseRpcContext } from './base-rpc.context'; +import { TcpSocket } from '../helpers/index.js'; +import { BaseRpcContext } from './base-rpc.context.js'; type TcpContextArgs = [TcpSocket, string]; diff --git a/packages/microservices/decorators/client.decorator.ts b/packages/microservices/decorators/client.decorator.ts index 48848b6a4de..bd9a21d3dd3 100644 --- a/packages/microservices/decorators/client.decorator.ts +++ b/packages/microservices/decorators/client.decorator.ts @@ -1,5 +1,8 @@ -import { CLIENT_CONFIGURATION_METADATA, CLIENT_METADATA } from '../constants'; -import { ClientOptions } from '../interfaces/client-metadata.interface'; +import { + CLIENT_CONFIGURATION_METADATA, + CLIENT_METADATA, +} from '../constants.js'; +import { ClientOptions } from '../interfaces/client-metadata.interface.js'; /** * Attaches the `ClientProxy` instance to the given property diff --git a/packages/microservices/decorators/ctx.decorator.ts b/packages/microservices/decorators/ctx.decorator.ts index 6e1619c6182..0ef681c1666 100644 --- a/packages/microservices/decorators/ctx.decorator.ts +++ b/packages/microservices/decorators/ctx.decorator.ts @@ -1,5 +1,5 @@ -import { RpcParamtype } from '../enums/rpc-paramtype.enum'; -import { createRpcParamDecorator } from '../utils/param.utils'; +import { RpcParamtype } from '../enums/rpc-paramtype.enum.js'; +import { createRpcParamDecorator } from '../utils/param.utils.js'; export const Ctx: () => ParameterDecorator = createRpcParamDecorator( RpcParamtype.CONTEXT, diff --git a/packages/microservices/decorators/event-pattern.decorator.ts b/packages/microservices/decorators/event-pattern.decorator.ts index f4973d741bf..73c75e833d6 100644 --- a/packages/microservices/decorators/event-pattern.decorator.ts +++ b/packages/microservices/decorators/event-pattern.decorator.ts @@ -1,17 +1,23 @@ -import { - isNil, - isNumber, - isObject, - isSymbol, -} from '@nestjs/common/utils/shared.utils'; import { PATTERN_EXTRAS_METADATA, PATTERN_HANDLER_METADATA, PATTERN_METADATA, TRANSPORT_METADATA, -} from '../constants'; -import { Transport } from '../enums'; -import { PatternHandler } from '../enums/pattern-handler.enum'; +} from '../constants.js'; +import { Transport } from '../enums/index.js'; +import { PatternHandler } from '../enums/pattern-handler.enum.js'; +import { isNil, isNumber, isObject, isSymbol } from '@nestjs/common/internal'; + +export type EventDataMethodDecorator< + TEventTypes extends Record, + TKey extends keyof TEventTypes, +> = ( + target: object, + propertyKey: string | symbol, + descriptor: TypedPropertyDescriptor< + (data: TEventTypes[TKey], ...args: unknown[]) => any + >, +) => void; /** * Subscribes to incoming events which fulfils chosen pattern. @@ -19,6 +25,22 @@ import { PatternHandler } from '../enums/pattern-handler.enum'; * @publicApi */ export const EventPattern: { + >( + topic: keyof TEventTypes, + ): EventDataMethodDecorator; + >( + topic: keyof TEventTypes, + transport: Transport | symbol, + ): EventDataMethodDecorator; + >( + topic: keyof TEventTypes, + extras: Record, + ): EventDataMethodDecorator; + >( + topic: keyof TEventTypes, + transport: Transport | symbol, + extras: Record, + ): EventDataMethodDecorator; (metadata?: T): MethodDecorator; (metadata?: T, transport?: Transport | symbol): MethodDecorator; (metadata?: T, extras?: Record): MethodDecorator; diff --git a/packages/microservices/decorators/index.ts b/packages/microservices/decorators/index.ts index 0ef11336042..3624abc877b 100644 --- a/packages/microservices/decorators/index.ts +++ b/packages/microservices/decorators/index.ts @@ -1,6 +1,6 @@ -export * from './client.decorator'; -export * from './ctx.decorator'; -export * from './event-pattern.decorator'; -export * from './grpc-service.decorator'; -export * from './message-pattern.decorator'; -export * from './payload.decorator'; +export * from './client.decorator.js'; +export * from './ctx.decorator.js'; +export * from './event-pattern.decorator.js'; +export * from './grpc-service.decorator.js'; +export * from './message-pattern.decorator.js'; +export * from './payload.decorator.js'; diff --git a/packages/microservices/decorators/message-pattern.decorator.ts b/packages/microservices/decorators/message-pattern.decorator.ts index 99c4f3c609f..5b8b310ab3d 100644 --- a/packages/microservices/decorators/message-pattern.decorator.ts +++ b/packages/microservices/decorators/message-pattern.decorator.ts @@ -1,23 +1,17 @@ -import { - isNil, - isNumber, - isObject, - isSymbol, -} from '@nestjs/common/utils/shared.utils'; - import { PATTERN_EXTRAS_METADATA, PATTERN_HANDLER_METADATA, PATTERN_METADATA, TRANSPORT_METADATA, -} from '../constants'; -import { Transport } from '../enums'; -import { PatternHandler } from '../enums/pattern-handler.enum'; +} from '../constants.js'; +import { Transport } from '../enums/index.js'; +import { PatternHandler } from '../enums/pattern-handler.enum.js'; import { InvalidGrpcDecoratorException, RpcDecoratorMetadata, -} from '../errors/invalid-grpc-message-decorator.exception'; -import { PatternMetadata } from '../interfaces/pattern-metadata.interface'; +} from '../errors/invalid-grpc-message-decorator.exception.js'; +import { PatternMetadata } from '../interfaces/pattern-metadata.interface.js'; +import { isNil, isNumber, isObject, isSymbol } from '@nestjs/common/internal'; export enum GrpcMethodStreamingType { NO_STREAMING = 'no_stream', diff --git a/packages/microservices/decorators/payload.decorator.ts b/packages/microservices/decorators/payload.decorator.ts index 4eb2de3da82..e4837c729f9 100644 --- a/packages/microservices/decorators/payload.decorator.ts +++ b/packages/microservices/decorators/payload.decorator.ts @@ -1,6 +1,10 @@ -import { PipeTransform, Type } from '@nestjs/common'; -import { RpcParamtype } from '../enums/rpc-paramtype.enum'; -import { createPipesRpcParamDecorator } from '../utils/param.utils'; +import type { + ParameterDecoratorOptions, + PipeTransform, + Type, +} from '@nestjs/common'; +import { RpcParamtype } from '../enums/rpc-paramtype.enum.js'; +import { createPipesRpcParamDecorator } from '../utils/param.utils.js'; /** * Microservice message pattern payload parameter decorator. @@ -51,12 +55,49 @@ export function Payload( propertyKey?: string, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator; +/** + * Microservice message pattern payload parameter decorator. Extracts a property from the + * payload object with additional options. + * + * For example, extracting a single param with schema: + * ```typescript + * create(@Payload('data', { schema: z.string() }) data: string) + * ``` + * @param propertyKey name of single property to extract from the message payload + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @publicApi + */ +export function Payload( + propertyKey: string, + options: ParameterDecoratorOptions, +): ParameterDecorator; +/** + * Microservice message pattern payload parameter decorator. + * + * For example, passing schema as options: + * ```typescript + * create(@Payload({ schema: z.object({ data: z.string() }) }) payload) + * ``` + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @publicApi + */ +export function Payload(options: ParameterDecoratorOptions): ParameterDecorator; export function Payload( - propertyOrPipe?: string | (Type | PipeTransform), + propertyOrPipe?: + | string + | (Type | PipeTransform) + | ParameterDecoratorOptions, + optionsOrPipe?: + | ParameterDecoratorOptions + | Type + | PipeTransform, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator { return createPipesRpcParamDecorator(RpcParamtype.PAYLOAD)( propertyOrPipe, + optionsOrPipe, ...pipes, ); } diff --git a/packages/microservices/deserializers/identity.deserializer.ts b/packages/microservices/deserializers/identity.deserializer.ts index 7a6d1e0d41e..6743fdb2dc1 100644 --- a/packages/microservices/deserializers/identity.deserializer.ts +++ b/packages/microservices/deserializers/identity.deserializer.ts @@ -1,4 +1,4 @@ -import { Deserializer } from '../interfaces/deserializer.interface'; +import { Deserializer } from '../interfaces/deserializer.interface.js'; /** * @publicApi diff --git a/packages/microservices/deserializers/incoming-request.deserializer.ts b/packages/microservices/deserializers/incoming-request.deserializer.ts index 537e9b71529..60286c3f0a1 100644 --- a/packages/microservices/deserializers/incoming-request.deserializer.ts +++ b/packages/microservices/deserializers/incoming-request.deserializer.ts @@ -1,9 +1,9 @@ -import { isUndefined } from '@nestjs/common/utils/shared.utils'; import { ConsumerDeserializer, IncomingEvent, IncomingRequest, -} from '../interfaces'; +} from '../interfaces/index.js'; +import { isUndefined } from '@nestjs/common/internal'; /** * @publicApi @@ -12,7 +12,10 @@ export class IncomingRequestDeserializer implements ConsumerDeserializer { deserialize( value: any, options?: Record, - ): IncomingRequest | IncomingEvent { + ): + | IncomingRequest + | IncomingEvent + | Promise { return this.isExternal(value) ? this.mapToSchema(value, options) : value; } diff --git a/packages/microservices/deserializers/incoming-response.deserializer.ts b/packages/microservices/deserializers/incoming-response.deserializer.ts index 55ecc468464..f78155b8d5f 100644 --- a/packages/microservices/deserializers/incoming-response.deserializer.ts +++ b/packages/microservices/deserializers/incoming-response.deserializer.ts @@ -1,11 +1,14 @@ -import { isUndefined } from '@nestjs/common/utils/shared.utils'; -import { IncomingResponse, ProducerDeserializer } from '../interfaces'; +import { IncomingResponse, ProducerDeserializer } from '../interfaces/index.js'; +import { isUndefined } from '@nestjs/common/internal'; /** * @publicApi */ export class IncomingResponseDeserializer implements ProducerDeserializer { - deserialize(value: any, options?: Record): IncomingResponse { + deserialize( + value: any, + options?: Record, + ): IncomingResponse | Promise { return this.isExternal(value) ? this.mapToSchema(value) : value; } diff --git a/packages/microservices/deserializers/index.ts b/packages/microservices/deserializers/index.ts index 21fdbeb264e..2356b64f729 100644 --- a/packages/microservices/deserializers/index.ts +++ b/packages/microservices/deserializers/index.ts @@ -1,7 +1,7 @@ -export * from './identity.deserializer'; -export * from './incoming-request.deserializer'; -export * from './incoming-response.deserializer'; -export * from './kafka-request.deserializer'; -export * from './kafka-response.deserializer'; -export * from './nats-request-json.deserializer'; -export * from './nats-response-json.deserializer'; +export * from './identity.deserializer.js'; +export * from './incoming-request.deserializer.js'; +export * from './incoming-response.deserializer.js'; +export * from './kafka-request.deserializer.js'; +export * from './kafka-response.deserializer.js'; +export * from './nats-request-json.deserializer.js'; +export * from './nats-response-json.deserializer.js'; diff --git a/packages/microservices/deserializers/kafka-request.deserializer.ts b/packages/microservices/deserializers/kafka-request.deserializer.ts index 359321728fe..e0065deab16 100644 --- a/packages/microservices/deserializers/kafka-request.deserializer.ts +++ b/packages/microservices/deserializers/kafka-request.deserializer.ts @@ -1,6 +1,6 @@ -import { IncomingEvent, IncomingRequest } from '../interfaces'; -import { KafkaRequest } from '../serializers/kafka-request.serializer'; -import { IncomingRequestDeserializer } from './incoming-request.deserializer'; +import { IncomingEvent, IncomingRequest } from '../interfaces/index.js'; +import { KafkaRequest } from '../serializers/kafka-request.serializer.js'; +import { IncomingRequestDeserializer } from './incoming-request.deserializer.js'; /** * @publicApi diff --git a/packages/microservices/deserializers/kafka-response.deserializer.ts b/packages/microservices/deserializers/kafka-response.deserializer.ts index 81a28e9afec..bad3e095efd 100644 --- a/packages/microservices/deserializers/kafka-response.deserializer.ts +++ b/packages/microservices/deserializers/kafka-response.deserializer.ts @@ -1,6 +1,6 @@ -import { isUndefined } from '@nestjs/common/utils/shared.utils'; -import { KafkaHeaders } from '../enums/kafka-headers.enum'; -import { Deserializer, IncomingResponse } from '../interfaces'; +import { KafkaHeaders } from '../enums/kafka-headers.enum.js'; +import { Deserializer, IncomingResponse } from '../interfaces/index.js'; +import { isUndefined } from '@nestjs/common/internal'; /** * @publicApi diff --git a/packages/microservices/deserializers/nats-request-json.deserializer.ts b/packages/microservices/deserializers/nats-request-json.deserializer.ts index e0311041ace..54547777d10 100644 --- a/packages/microservices/deserializers/nats-request-json.deserializer.ts +++ b/packages/microservices/deserializers/nats-request-json.deserializer.ts @@ -1,30 +1,27 @@ -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { NatsCodec } from '../external/nats-codec.interface'; -import { IncomingEvent, IncomingRequest } from '../interfaces'; -import { IncomingRequestDeserializer } from './incoming-request.deserializer'; +import { IncomingEvent, IncomingRequest } from '../interfaces/index.js'; +import { IncomingRequestDeserializer } from './incoming-request.deserializer.js'; -let natsPackage = {} as any; +// To enable type safety for Nats. This cant be uncommented by default +// because it would require the user to install the nats package even if they dont use Nats +// Otherwise, TypeScript would fail to compile the code. +// +// type NatsMsg = import('@nats-io/transport-node').Msg; + +type NatsMsg = any; /** * @publicApi */ export class NatsRequestJSONDeserializer extends IncomingRequestDeserializer { - private readonly jsonCodec: NatsCodec; - - constructor() { - super(); - - natsPackage = loadPackage('nats', NatsRequestJSONDeserializer.name, () => - require('nats'), - ); - this.jsonCodec = natsPackage.JSONCodec(); - } - deserialize( - value: Uint8Array, + value: NatsMsg, options?: Record, - ): IncomingRequest | IncomingEvent { - const decodedRequest = this.jsonCodec.decode(value); + ): + | IncomingRequest + | IncomingEvent + | Promise + | Promise { + const decodedRequest = value.json(); return super.deserialize(decodedRequest, options); } } diff --git a/packages/microservices/deserializers/nats-response-json.deserializer.ts b/packages/microservices/deserializers/nats-response-json.deserializer.ts index 5b81f7512ee..4ebacc7fec1 100644 --- a/packages/microservices/deserializers/nats-response-json.deserializer.ts +++ b/packages/microservices/deserializers/nats-response-json.deserializer.ts @@ -1,31 +1,23 @@ -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { NatsCodec } from '../external/nats-codec.interface'; -import { IncomingResponse } from '../interfaces'; -import { IncomingResponseDeserializer } from './incoming-response.deserializer'; -import { NatsRequestJSONDeserializer } from './nats-request-json.deserializer'; +import { IncomingResponse } from '../interfaces/index.js'; +import { IncomingResponseDeserializer } from './incoming-response.deserializer.js'; -let natsPackage = {} as any; +// To enable type safety for Nats. This cant be uncommented by default +// because it would require the user to install the nats package even if they dont use Nats +// Otherwise, TypeScript would fail to compile the code. +// +// type NatsMsg = import('@nats-io/transport-node').Msg; + +type NatsMsg = any; /** * @publicApi */ export class NatsResponseJSONDeserializer extends IncomingResponseDeserializer { - private readonly jsonCodec: NatsCodec; - - constructor() { - super(); - - natsPackage = loadPackage('nats', NatsRequestJSONDeserializer.name, () => - require('nats'), - ); - this.jsonCodec = natsPackage.JSONCodec(); - } - deserialize( - value: Uint8Array, + value: NatsMsg, options?: Record, - ): IncomingResponse { - const decodedRequest = this.jsonCodec.decode(value); + ): IncomingResponse | Promise { + const decodedRequest = value.json(); return super.deserialize(decodedRequest, options); } } diff --git a/packages/microservices/enums/index.ts b/packages/microservices/enums/index.ts index 1ce135f23c2..f8bacab7e82 100644 --- a/packages/microservices/enums/index.ts +++ b/packages/microservices/enums/index.ts @@ -1,2 +1,2 @@ -export * from './transport.enum'; -export * from './kafka-headers.enum'; +export * from './transport.enum.js'; +export * from './kafka-headers.enum.js'; diff --git a/packages/microservices/enums/rpc-paramtype.enum.ts b/packages/microservices/enums/rpc-paramtype.enum.ts index c857e9b49f9..ea7e8e624ea 100644 --- a/packages/microservices/enums/rpc-paramtype.enum.ts +++ b/packages/microservices/enums/rpc-paramtype.enum.ts @@ -1,4 +1,4 @@ -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; +import { RouteParamtypes } from '@nestjs/common/internal'; export enum RpcParamtype { PAYLOAD = RouteParamtypes.BODY, diff --git a/packages/microservices/errors/invalid-grpc-message-decorator.exception.ts b/packages/microservices/errors/invalid-grpc-message-decorator.exception.ts index ffb4c45342d..40ef3f3c35a 100644 --- a/packages/microservices/errors/invalid-grpc-message-decorator.exception.ts +++ b/packages/microservices/errors/invalid-grpc-message-decorator.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; export interface RpcDecoratorMetadata { service: string; diff --git a/packages/microservices/errors/invalid-grpc-package-definition-missing-package-definition.exception.ts b/packages/microservices/errors/invalid-grpc-package-definition-missing-package-definition.exception.ts index c5483358ec2..ef6948036d5 100644 --- a/packages/microservices/errors/invalid-grpc-package-definition-missing-package-definition.exception.ts +++ b/packages/microservices/errors/invalid-grpc-package-definition-missing-package-definition.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; export class InvalidGrpcPackageDefinitionMissingPackageDefinitionException extends RuntimeException { constructor() { diff --git a/packages/microservices/errors/invalid-grpc-package-definition-mutex.exception.ts b/packages/microservices/errors/invalid-grpc-package-definition-mutex.exception.ts index baa4f8be71f..8be905896fb 100644 --- a/packages/microservices/errors/invalid-grpc-package-definition-mutex.exception.ts +++ b/packages/microservices/errors/invalid-grpc-package-definition-mutex.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; export class InvalidGrpcPackageDefinitionMutexException extends RuntimeException { constructor() { diff --git a/packages/microservices/errors/invalid-grpc-package.exception.ts b/packages/microservices/errors/invalid-grpc-package.exception.ts index a0599e45737..6534f30d1d4 100644 --- a/packages/microservices/errors/invalid-grpc-package.exception.ts +++ b/packages/microservices/errors/invalid-grpc-package.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/microservices/errors/invalid-grpc-service.exception.ts b/packages/microservices/errors/invalid-grpc-service.exception.ts index 1da6a18c8ca..f649eaf3f3f 100644 --- a/packages/microservices/errors/invalid-grpc-service.exception.ts +++ b/packages/microservices/errors/invalid-grpc-service.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/microservices/errors/invalid-kafka-client-topic.exception.ts b/packages/microservices/errors/invalid-kafka-client-topic.exception.ts index 12871369f04..28b9654befa 100644 --- a/packages/microservices/errors/invalid-kafka-client-topic.exception.ts +++ b/packages/microservices/errors/invalid-kafka-client-topic.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/microservices/errors/invalid-message.exception.ts b/packages/microservices/errors/invalid-message.exception.ts index 6b8c0e9eef8..8a0ba850283 100644 --- a/packages/microservices/errors/invalid-message.exception.ts +++ b/packages/microservices/errors/invalid-message.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/microservices/errors/invalid-proto-definition.exception.ts b/packages/microservices/errors/invalid-proto-definition.exception.ts index 28ce445a02b..a276d4c2018 100644 --- a/packages/microservices/errors/invalid-proto-definition.exception.ts +++ b/packages/microservices/errors/invalid-proto-definition.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/microservices/errors/invalid-tcp-data-reception.exception.ts b/packages/microservices/errors/invalid-tcp-data-reception.exception.ts index 31e69eba6f1..a9cc4b98966 100644 --- a/packages/microservices/errors/invalid-tcp-data-reception.exception.ts +++ b/packages/microservices/errors/invalid-tcp-data-reception.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; export class InvalidTcpDataReceptionException extends RuntimeException { constructor(err: string | Error) { diff --git a/packages/microservices/events/index.ts b/packages/microservices/events/index.ts index df8d4959f07..72afb0b5978 100644 --- a/packages/microservices/events/index.ts +++ b/packages/microservices/events/index.ts @@ -1,6 +1,6 @@ -export { KafkaStatus } from './kafka.events'; -export { MqttEvents, MqttStatus } from './mqtt.events'; -export { NatsEvents, NatsStatus } from './nats.events'; -export { RedisEvents, RedisStatus } from './redis.events'; -export { RmqEvents, RmqStatus } from './rmq.events'; -export { TcpEvents, TcpStatus } from './tcp.events'; +export { KafkaStatus } from './kafka.events.js'; +export { MqttEvents, MqttStatus } from './mqtt.events.js'; +export { NatsEvents, NatsStatus } from './nats.events.js'; +export { RedisEvents, RedisStatus } from './redis.events.js'; +export { RmqEvents, RmqStatus } from './rmq.events.js'; +export { TcpEvents, TcpStatus } from './tcp.events.js'; diff --git a/packages/microservices/exceptions/base-rpc-exception-filter.ts b/packages/microservices/exceptions/base-rpc-exception-filter.ts index 4cc92bc9453..2cf6d12eee7 100644 --- a/packages/microservices/exceptions/base-rpc-exception-filter.ts +++ b/packages/microservices/exceptions/base-rpc-exception-filter.ts @@ -1,13 +1,13 @@ import { - ArgumentsHost, + type ArgumentsHost, IntrinsicException, Logger, - RpcExceptionFilter, + type RpcExceptionFilter, } from '@nestjs/common'; -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { MESSAGES } from '@nestjs/core/constants'; import { Observable, throwError as _throw } from 'rxjs'; -import { RpcException } from './rpc-exception'; +import { RpcException } from './rpc-exception.js'; +import { isObject } from '@nestjs/common/internal'; +import { MESSAGES } from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/microservices/exceptions/index.ts b/packages/microservices/exceptions/index.ts index 73e4c3176b3..cdb16cb4987 100644 --- a/packages/microservices/exceptions/index.ts +++ b/packages/microservices/exceptions/index.ts @@ -1,3 +1,3 @@ -export * from './base-rpc-exception-filter'; -export * from './kafka-retriable-exception'; -export * from './rpc-exception'; +export * from './base-rpc-exception-filter.js'; +export * from './kafka-retriable-exception.js'; +export * from './rpc-exception.js'; diff --git a/packages/microservices/exceptions/kafka-retriable-exception.ts b/packages/microservices/exceptions/kafka-retriable-exception.ts index c62194a2e30..a6de62f5ac8 100644 --- a/packages/microservices/exceptions/kafka-retriable-exception.ts +++ b/packages/microservices/exceptions/kafka-retriable-exception.ts @@ -1,4 +1,4 @@ -import { RpcException } from './rpc-exception'; +import { RpcException } from './rpc-exception.js'; /** * Exception that instructs Kafka driver to instead of introspecting diff --git a/packages/microservices/exceptions/rpc-exception.ts b/packages/microservices/exceptions/rpc-exception.ts index 107574a74ee..f7632cab065 100644 --- a/packages/microservices/exceptions/rpc-exception.ts +++ b/packages/microservices/exceptions/rpc-exception.ts @@ -1,4 +1,4 @@ -import { isObject, isString } from '@nestjs/common/utils/shared.utils'; +import { isObject, isString } from '@nestjs/common/internal'; /** * @publicApi diff --git a/packages/microservices/exceptions/rpc-exceptions-handler.ts b/packages/microservices/exceptions/rpc-exceptions-handler.ts index f551bd953fa..c3183dd97e9 100644 --- a/packages/microservices/exceptions/rpc-exceptions-handler.ts +++ b/packages/microservices/exceptions/rpc-exceptions-handler.ts @@ -1,11 +1,13 @@ -import { RpcExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions'; -import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface'; -import { selectExceptionFilterMetadata } from '@nestjs/common/utils/select-exception-filter-metadata.util'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { InvalidExceptionFilterException } from '@nestjs/core/errors/exceptions/invalid-exception-filter.exception'; import { Observable } from 'rxjs'; -import { BaseRpcExceptionFilter } from './base-rpc-exception-filter'; -import { RpcException } from './rpc-exception'; +import { BaseRpcExceptionFilter } from './base-rpc-exception-filter.js'; +import { RpcException } from './rpc-exception.js'; +import { + type RpcExceptionFilterMetadata, + selectExceptionFilterMetadata, + isEmptyArray, +} from '@nestjs/common/internal'; +import type { ArgumentsHost } from '@nestjs/common'; +import { InvalidExceptionFilterException } from '@nestjs/core/internal'; /** * @publicApi @@ -35,7 +37,7 @@ export class RpcExceptionsHandler extends BaseRpcExceptionFilter { exception: T, host: ArgumentsHost, ): Observable | null { - if (isEmpty(this.filters)) { + if (isEmptyArray(this.filters)) { return null; } diff --git a/packages/microservices/factories/rpc-params-factory.ts b/packages/microservices/factories/rpc-params-factory.ts index af8c9e72b8f..ca2865db4b0 100644 --- a/packages/microservices/factories/rpc-params-factory.ts +++ b/packages/microservices/factories/rpc-params-factory.ts @@ -1,4 +1,4 @@ -import { RpcParamtype } from '../enums/rpc-paramtype.enum'; +import { RpcParamtype } from '../enums/rpc-paramtype.enum.js'; export class RpcParamsFactory { public exchangeKeyForValue( diff --git a/packages/microservices/helpers/grpc-helpers.ts b/packages/microservices/helpers/grpc-helpers.ts index 56bf9ad2463..954eed0bbba 100644 --- a/packages/microservices/helpers/grpc-helpers.ts +++ b/packages/microservices/helpers/grpc-helpers.ts @@ -1,6 +1,6 @@ -import { InvalidGrpcPackageDefinitionMissingPackageDefinitionException } from '../errors/invalid-grpc-package-definition-missing-package-definition.exception'; -import { InvalidGrpcPackageDefinitionMutexException } from '../errors/invalid-grpc-package-definition-mutex.exception'; -import { GrpcOptions } from '../interfaces'; +import { InvalidGrpcPackageDefinitionMissingPackageDefinitionException } from '../errors/invalid-grpc-package-definition-missing-package-definition.exception.js'; +import { InvalidGrpcPackageDefinitionMutexException } from '../errors/invalid-grpc-package-definition-mutex.exception.js'; +import { GrpcOptions } from '../interfaces/index.js'; export function getGrpcPackageDefinition( options: GrpcOptions['options'], diff --git a/packages/microservices/helpers/index.ts b/packages/microservices/helpers/index.ts index e395ce7dc81..152d98ccf48 100644 --- a/packages/microservices/helpers/index.ts +++ b/packages/microservices/helpers/index.ts @@ -1,6 +1,6 @@ -export * from './json-socket'; -export * from './kafka-logger'; -export * from './kafka-parser'; -export * from './kafka-reply-partition-assigner'; -export * from './tcp-socket'; -export * from './grpc-helpers'; +export * from './json-socket.js'; +export * from './kafka-logger.js'; +export * from './kafka-parser.js'; +export * from './kafka-reply-partition-assigner.js'; +export * from './tcp-socket.js'; +export * from './grpc-helpers.js'; diff --git a/packages/microservices/helpers/json-socket.ts b/packages/microservices/helpers/json-socket.ts index 2812408d11e..eed68d92987 100644 --- a/packages/microservices/helpers/json-socket.ts +++ b/packages/microservices/helpers/json-socket.ts @@ -1,8 +1,8 @@ import { Buffer } from 'buffer'; import { StringDecoder } from 'string_decoder'; -import { CorruptedPacketLengthException } from '../errors/corrupted-packet-length.exception'; -import { MaxPacketLengthExceededException } from '../errors/max-packet-length-exceeded.exception'; -import { TcpSocket } from './tcp-socket'; +import { CorruptedPacketLengthException } from '../errors/corrupted-packet-length.exception.js'; +import { MaxPacketLengthExceededException } from '../errors/max-packet-length-exceeded.exception.js'; +import { TcpSocket } from './tcp-socket.js'; const DEFAULT_MAX_BUFFER_SIZE = (512 * 1024 * 1024) / 4; // 512 MBs in characters with 4 bytes per character (32-bit) diff --git a/packages/microservices/helpers/kafka-logger.ts b/packages/microservices/helpers/kafka-logger.ts index 64b49606087..0e41f815f73 100644 --- a/packages/microservices/helpers/kafka-logger.ts +++ b/packages/microservices/helpers/kafka-logger.ts @@ -1,4 +1,4 @@ -import { logLevel } from '../external/kafka.interface'; +import { logLevel } from '../external/kafka.interface.js'; export const KafkaLogger = (logger: any) => diff --git a/packages/microservices/helpers/kafka-parser.ts b/packages/microservices/helpers/kafka-parser.ts index 1d40e94d1c1..ff4192363b1 100644 --- a/packages/microservices/helpers/kafka-parser.ts +++ b/packages/microservices/helpers/kafka-parser.ts @@ -1,5 +1,5 @@ -import { isNil } from '@nestjs/common/utils/shared.utils'; -import { KafkaParserConfig } from '../interfaces'; +import { KafkaParserConfig } from '../interfaces/index.js'; +import { isNil } from '@nestjs/common/internal'; export class KafkaParser { protected readonly keepBinary: boolean; @@ -23,10 +23,9 @@ export class KafkaParser { result.key = this.decode(data.key); } if (!isNil(data.headers)) { - const decodeHeaderByKey = (key: string) => { + for (const key of Object.keys(data.headers)) { result.headers[key] = this.decode(data.headers[key]); - }; - Object.keys(data.headers).forEach(decodeHeaderByKey); + } } else { result.headers = {}; } diff --git a/packages/microservices/helpers/kafka-reply-partition-assigner.ts b/packages/microservices/helpers/kafka-reply-partition-assigner.ts index 955c96b6c95..04622f472f2 100644 --- a/packages/microservices/helpers/kafka-reply-partition-assigner.ts +++ b/packages/microservices/helpers/kafka-reply-partition-assigner.ts @@ -1,13 +1,13 @@ -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isUndefined } from '@nestjs/common/utils/shared.utils'; -import { ClientKafka } from '../client/client-kafka'; +import { createRequire } from 'module'; +import { ClientKafka } from '../client/client-kafka.js'; import { Cluster, GroupMember, GroupMemberAssignment, GroupState, MemberMetadata, -} from '../external/kafka.interface'; +} from '../external/kafka.interface.js'; +import { loadPackageSync, isUndefined } from '@nestjs/common/internal'; let kafkaPackage: any = {}; @@ -21,10 +21,10 @@ export class KafkaReplyPartitionAssigner { cluster: Cluster; }, ) { - kafkaPackage = loadPackage( + kafkaPackage = loadPackageSync( 'kafkajs', KafkaReplyPartitionAssigner.name, - () => require('kafkajs'), + () => createRequire(import.meta.url)('kafkajs'), ); } @@ -39,6 +39,7 @@ export class KafkaReplyPartitionAssigner { members: GroupMember[]; topics: string[]; }): Promise { + kafkaPackage = await kafkaPackage; const assignment = {}; const previousAssignment = {}; @@ -61,18 +62,14 @@ export class KafkaReplyPartitionAssigner { }); // build a collection of topics and partitions - const topicsPartitions = group.topics - .map(topic => { - const partitionMetadata = - this.config.cluster.findTopicPartitionMetadata(topic); - return partitionMetadata.map(m => { - return { - topic, - partitionId: m.partitionId, - }; - }); - }) - .reduce((acc, val) => acc.concat(val), []); + const topicsPartitions = group.topics.flatMap(topic => { + const partitionMetadata = + this.config.cluster.findTopicPartitionMetadata(topic); + return partitionMetadata.map(m => ({ + topic, + partitionId: m.partitionId, + })); + }); // create the new assignment by populating the members with the first partition of the topics sortedMemberIds.forEach(assignee => { @@ -188,6 +185,8 @@ export class KafkaReplyPartitionAssigner { } public decodeMember(member: GroupMember) { + // kafkaPackage must be resolved before calling this method + // (it is resolved in assign() which calls decodeMember) const memberMetadata = kafkaPackage.AssignerProtocol.MemberMetadata.decode( member.memberMetadata, ) as MemberMetadata; diff --git a/packages/microservices/helpers/tcp-socket.ts b/packages/microservices/helpers/tcp-socket.ts index cd4988fc1ce..9b75ce4bafa 100644 --- a/packages/microservices/helpers/tcp-socket.ts +++ b/packages/microservices/helpers/tcp-socket.ts @@ -1,8 +1,8 @@ import { Buffer } from 'buffer'; import { Socket } from 'net'; -import { InvalidJSONFormatException } from '../errors/invalid-json-format.exception'; -import { NetSocketClosedException } from '../errors/net-socket-closed.exception'; -import { TcpEventsMap } from '../events/tcp.events'; +import { InvalidJSONFormatException } from '../errors/invalid-json-format.exception.js'; +import { NetSocketClosedException } from '../errors/net-socket-closed.exception.js'; +import { TcpEventsMap } from '../events/tcp.events.js'; export abstract class TcpSocket { private isClosed = false; diff --git a/packages/microservices/index.ts b/packages/microservices/index.ts index 26b67b9262a..9053d1e294a 100644 --- a/packages/microservices/index.ts +++ b/packages/microservices/index.ts @@ -6,16 +6,16 @@ */ import 'reflect-metadata'; -export * from './client'; -export * from './ctx-host'; -export * from './decorators'; -export * from './enums'; -export * from './events'; -export * from './exceptions'; -export * from './helpers'; -export * from './interfaces'; -export * from './module'; -export * from './nest-microservice'; -export * from './record-builders'; -export * from './server'; -export * from './tokens'; +export * from './client/index.js'; +export * from './ctx-host/index.js'; +export * from './decorators/index.js'; +export * from './enums/index.js'; +export * from './events/index.js'; +export * from './exceptions/index.js'; +export * from './helpers/index.js'; +export * from './interfaces/index.js'; +export * from './module/index.js'; +export * from './nest-microservice.js'; +export * from './record-builders/index.js'; +export * from './server/index.js'; +export * from './tokens.js'; diff --git a/packages/microservices/interfaces/client-kafka-proxy.interface.ts b/packages/microservices/interfaces/client-kafka-proxy.interface.ts index 8a797cd0cf8..10fd806dc46 100644 --- a/packages/microservices/interfaces/client-kafka-proxy.interface.ts +++ b/packages/microservices/interfaces/client-kafka-proxy.interface.ts @@ -1,10 +1,10 @@ -import { ClientProxy } from '../client'; -import { KafkaStatus } from '../events'; +import { ClientProxy } from '../client/index.js'; +import { KafkaStatus } from '../events/index.js'; import { Consumer, Producer, TopicPartitionOffsetAndMetadata, -} from '../external/kafka.interface'; +} from '../external/kafka.interface.js'; export interface ClientKafkaProxy extends Omit< ClientProxy, diff --git a/packages/microservices/interfaces/client-metadata.interface.ts b/packages/microservices/interfaces/client-metadata.interface.ts index 415f54c2e4f..378e30edfae 100644 --- a/packages/microservices/interfaces/client-metadata.interface.ts +++ b/packages/microservices/interfaces/client-metadata.interface.ts @@ -1,9 +1,9 @@ -import { Type } from '@nestjs/common'; +import type { Type } from '@nestjs/common'; import { ConnectionOptions } from 'tls'; -import { ClientProxy } from '../client'; -import { Transport } from '../enums/transport.enum'; -import { TcpSocket } from '../helpers'; -import { Deserializer } from './deserializer.interface'; +import { ClientProxy } from '../client/index.js'; +import { Transport } from '../enums/transport.enum.js'; +import { TcpSocket } from '../helpers/index.js'; +import { Deserializer } from './deserializer.interface.js'; import { GrpcOptions, KafkaOptions, @@ -11,8 +11,8 @@ import { NatsOptions, RedisOptions, RmqOptions, -} from './microservice-configuration.interface'; -import { Serializer } from './serializer.interface'; +} from './microservice-configuration.interface.js'; +import { Serializer } from './serializer.interface.js'; export type ClientOptions = | RedisOptions diff --git a/packages/microservices/interfaces/custom-transport-strategy.interface.ts b/packages/microservices/interfaces/custom-transport-strategy.interface.ts index 507db2e4393..c23ce7943c5 100644 --- a/packages/microservices/interfaces/custom-transport-strategy.interface.ts +++ b/packages/microservices/interfaces/custom-transport-strategy.interface.ts @@ -1,4 +1,4 @@ -import { TransportId } from './microservice-configuration.interface'; +import { TransportId } from './microservice-configuration.interface.js'; /** * @publicApi diff --git a/packages/microservices/interfaces/deserializer.interface.ts b/packages/microservices/interfaces/deserializer.interface.ts index d5529b697ff..c1f2974a68d 100644 --- a/packages/microservices/interfaces/deserializer.interface.ts +++ b/packages/microservices/interfaces/deserializer.interface.ts @@ -2,7 +2,7 @@ import { IncomingEvent, IncomingRequest, IncomingResponse, -} from './packet.interface'; +} from './packet.interface.js'; /** * @publicApi diff --git a/packages/microservices/interfaces/index.ts b/packages/microservices/interfaces/index.ts index 71718b8e1b1..716e7ddc3dc 100644 --- a/packages/microservices/interfaces/index.ts +++ b/packages/microservices/interfaces/index.ts @@ -1,12 +1,12 @@ -export * from './client-grpc.interface'; -export * from './client-kafka-proxy.interface'; -export * from './client-metadata.interface'; -export * from './custom-transport-strategy.interface'; -export * from './deserializer.interface'; -export * from './message-handler.interface'; -export * from './microservice-configuration.interface'; -export * from './packet.interface'; -export * from './pattern-metadata.interface'; -export * from './pattern.interface'; -export * from './request-context.interface'; -export * from './serializer.interface'; +export * from './client-grpc.interface.js'; +export * from './client-kafka-proxy.interface.js'; +export * from './client-metadata.interface.js'; +export * from './custom-transport-strategy.interface.js'; +export * from './deserializer.interface.js'; +export * from './message-handler.interface.js'; +export * from './microservice-configuration.interface.js'; +export * from './packet.interface.js'; +export * from './pattern-metadata.interface.js'; +export * from './pattern.interface.js'; +export * from './request-context.interface.js'; +export * from './serializer.interface.js'; diff --git a/packages/microservices/interfaces/microservice-configuration.interface.ts b/packages/microservices/interfaces/microservice-configuration.interface.ts index 2e90a6592da..b7232530e08 100644 --- a/packages/microservices/interfaces/microservice-configuration.interface.ts +++ b/packages/microservices/interfaces/microservice-configuration.interface.ts @@ -1,7 +1,7 @@ -import { InjectionToken, Type } from '@nestjs/common'; +import type { InjectionToken, Type } from '@nestjs/common'; import { TlsOptions } from 'tls'; -import { Transport } from '../enums/transport.enum'; -import { ChannelOptions } from '../external/grpc-options.interface'; +import { Transport } from '../enums/transport.enum.js'; +import { ChannelOptions } from '../external/grpc-options.interface.js'; import { ConsumerConfig, ConsumerRunConfig, @@ -9,18 +9,18 @@ import { KafkaConfig, ProducerConfig, ProducerRecord, -} from '../external/kafka.interface'; -import { MqttClientOptions, QoS } from '../external/mqtt-options.interface'; -import { IORedisOptions } from '../external/redis.interface'; +} from '../external/kafka.interface.js'; +import { MqttClientOptions, QoS } from '../external/mqtt-options.interface.js'; +import { IORedisOptions } from '../external/redis.interface.js'; import { AmqpConnectionManagerSocketOptions, AmqplibQueueOptions, RmqUrl, -} from '../external/rmq-url.interface'; -import { TcpSocket } from '../helpers'; -import { CustomTransportStrategy } from './custom-transport-strategy.interface'; -import { Deserializer } from './deserializer.interface'; -import { Serializer } from './serializer.interface'; +} from '../external/rmq-url.interface.js'; +import { TcpSocket } from '../helpers/index.js'; +import { CustomTransportStrategy } from './custom-transport-strategy.interface.js'; +import { Deserializer } from './deserializer.interface.js'; +import { Serializer } from './serializer.interface.js'; export type MicroserviceOptions = | GrpcOptions diff --git a/packages/microservices/interfaces/microservice-entrypoint-metadata.interface.ts b/packages/microservices/interfaces/microservice-entrypoint-metadata.interface.ts index 9993c7d7701..f747374c700 100644 --- a/packages/microservices/interfaces/microservice-entrypoint-metadata.interface.ts +++ b/packages/microservices/interfaces/microservice-entrypoint-metadata.interface.ts @@ -1,5 +1,5 @@ -import { Transport } from '../enums'; -import { PatternMetadata } from './pattern-metadata.interface'; +import { Transport } from '../enums/index.js'; +import { PatternMetadata } from './pattern-metadata.interface.js'; export type MicroserviceEntrypointMetadata = { transportId: keyof typeof Transport | symbol; diff --git a/packages/microservices/interfaces/request-context.interface.ts b/packages/microservices/interfaces/request-context.interface.ts index 4b207231e9a..6411b012091 100644 --- a/packages/microservices/interfaces/request-context.interface.ts +++ b/packages/microservices/interfaces/request-context.interface.ts @@ -1,4 +1,4 @@ -import { BaseRpcContext } from '../ctx-host/base-rpc.context'; +import { BaseRpcContext } from '../ctx-host/base-rpc.context.js'; export interface RequestContext< TData = any, diff --git a/packages/microservices/interfaces/serializer.interface.ts b/packages/microservices/interfaces/serializer.interface.ts index 4c090467c8b..681064df005 100644 --- a/packages/microservices/interfaces/serializer.interface.ts +++ b/packages/microservices/interfaces/serializer.interface.ts @@ -2,13 +2,16 @@ import { OutgoingEvent, OutgoingRequest, OutgoingResponse, -} from './packet.interface'; +} from './packet.interface.js'; /** * @publicApi */ export interface Serializer { - serialize(value: TInput, options?: Record): TOutput; + serialize( + value: TInput, + options?: Record, + ): TOutput | Promise; } export type ProducerSerializer = Serializer< diff --git a/packages/microservices/listener-metadata-explorer.ts b/packages/microservices/listener-metadata-explorer.ts index e0fdd9c033e..55269452809 100644 --- a/packages/microservices/listener-metadata-explorer.ts +++ b/packages/microservices/listener-metadata-explorer.ts @@ -1,6 +1,3 @@ -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { isFunction, isUndefined } from '@nestjs/common/utils/shared.utils'; -import { MetadataScanner } from '@nestjs/core/metadata-scanner'; import { CLIENT_CONFIGURATION_METADATA, CLIENT_METADATA, @@ -8,10 +5,16 @@ import { PATTERN_HANDLER_METADATA, PATTERN_METADATA, TRANSPORT_METADATA, -} from './constants'; -import { Transport } from './enums'; -import { PatternHandler } from './enums/pattern-handler.enum'; -import { ClientOptions, PatternMetadata } from './interfaces'; +} from './constants.js'; +import { Transport } from './enums/index.js'; +import { PatternHandler } from './enums/pattern-handler.enum.js'; +import { ClientOptions, PatternMetadata } from './interfaces/index.js'; +import { + type Controller, + isFunction, + isUndefined, +} from '@nestjs/common/internal'; +import type { MetadataScanner } from '@nestjs/core'; export interface ClientProperties { property: string; diff --git a/packages/microservices/listeners-controller.ts b/packages/microservices/listeners-controller.ts index 86f556e330c..9713ffbc849 100644 --- a/packages/microservices/listeners-controller.ts +++ b/packages/microservices/listeners-controller.ts @@ -1,18 +1,3 @@ -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { isUndefined } from '@nestjs/common/utils/shared.utils'; -import { ContextIdFactory } from '@nestjs/core/helpers/context-id-factory'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { STATIC_CONTEXT } from '@nestjs/core/injector/constants'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { Injector } from '@nestjs/core/injector/injector'; -import { - ContextId, - InstanceWrapper, -} from '@nestjs/core/injector/instance-wrapper'; -import { Module } from '@nestjs/core/injector/module'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; -import { MetadataScanner } from '@nestjs/core/metadata-scanner'; -import { REQUEST_CONTEXT_ID } from '@nestjs/core/router/request/request-constants'; import { forkJoin, from as fromPromise, @@ -22,25 +7,45 @@ import { ObservedValueOf, of, } from 'rxjs'; -import { IClientProxyFactory } from './client/client-proxy-factory'; -import { ClientsContainer } from './container'; -import { ExceptionFiltersContext } from './context/exception-filters-context'; -import { RequestContextHost } from './context/request-context-host'; -import { RpcContextCreator } from './context/rpc-context-creator'; +import { IClientProxyFactory } from './client/client-proxy-factory.js'; +import { ClientsContainer } from './container.js'; +import { ExceptionFiltersContext } from './context/exception-filters-context.js'; +import { RequestContextHost } from './context/request-context-host.js'; +import { RpcContextCreator } from './context/rpc-context-creator.js'; import { DEFAULT_CALLBACK_METADATA, DEFAULT_GRPC_CALLBACK_METADATA, -} from './context/rpc-metadata-constants'; -import { BaseRpcContext } from './ctx-host/base-rpc.context'; -import { Transport } from './enums'; -import { MessageHandler, PatternMetadata, RequestContext } from './interfaces'; -import { MicroserviceEntrypointMetadata } from './interfaces/microservice-entrypoint-metadata.interface'; +} from './context/rpc-metadata-constants.js'; +import { BaseRpcContext } from './ctx-host/base-rpc.context.js'; +import { Transport } from './enums/index.js'; +import { + MessageHandler, + PatternMetadata, + RequestContext, +} from './interfaces/index.js'; +import { MicroserviceEntrypointMetadata } from './interfaces/microservice-entrypoint-metadata.interface.js'; import { EventOrMessageListenerDefinition, ListenerMetadataExplorer, -} from './listener-metadata-explorer'; -import { ServerGrpc } from './server'; -import { Server } from './server/server'; +} from './listener-metadata-explorer.js'; +import { ServerGrpc } from './server/index.js'; +import { Server } from './server/server.js'; +import { type Controller, isUndefined } from '@nestjs/common/internal'; +import { + ContextIdFactory, + type NestContainer, + type ContextId, + type GraphInspector, + MetadataScanner, +} from '@nestjs/core'; +import { + ExecutionContextHost, + STATIC_CONTEXT, + type Injector, + type InstanceWrapper, + type Module, + REQUEST_CONTEXT_ID, +} from '@nestjs/core/internal'; export class ListenersController { private readonly metadataExplorer = new ListenerMetadataExplorer( @@ -80,12 +85,12 @@ export class ListenersController { isUndefined(serverInstance.transportId) || transport === serverInstance.transportId, ) - .reduce((acc, handler) => { - handler.patterns.forEach(pattern => - acc.push({ ...handler, patterns: [pattern] }), - ); - return acc; - }, [] as EventOrMessageListenerDefinition[]) + .flatMap(handler => + handler.patterns.map(pattern => ({ + ...handler, + patterns: [pattern], + })), + ) .forEach((definition: EventOrMessageListenerDefinition) => { const { patterns: [pattern], diff --git a/packages/microservices/microservices-module.ts b/packages/microservices/microservices-module.ts index 16f40346cc5..072316f6daf 100644 --- a/packages/microservices/microservices-module.ts +++ b/packages/microservices/microservices-module.ts @@ -1,24 +1,30 @@ -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; -import { GuardsConsumer, GuardsContextCreator } from '@nestjs/core/guards'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { Injector } from '@nestjs/core/injector/injector'; -import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; +import { ClientProxyFactory } from './client/index.js'; +import { ClientsContainer } from './container.js'; +import { ExceptionFiltersContext } from './context/exception-filters-context.js'; +import { RpcContextCreator } from './context/rpc-context-creator.js'; +import { RpcProxy } from './context/rpc-proxy.js'; +import { ListenersController } from './listeners-controller.js'; +import { Server } from './server/server.js'; +import type { + Controller, + NestApplicationContextOptions, +} from '@nestjs/common/internal'; +import type { + ApplicationConfig, + NestContainer, + GraphInspector, +} from '@nestjs/core'; import { + RuntimeException, + GuardsConsumer, + GuardsContextCreator, + Injector, + type InstanceWrapper, InterceptorsConsumer, InterceptorsContextCreator, -} from '@nestjs/core/interceptors'; -import { PipesConsumer, PipesContextCreator } from '@nestjs/core/pipes'; -import { ClientProxyFactory } from './client'; -import { ClientsContainer } from './container'; -import { ExceptionFiltersContext } from './context/exception-filters-context'; -import { RpcContextCreator } from './context/rpc-context-creator'; -import { RpcProxy } from './context/rpc-proxy'; -import { ListenersController } from './listeners-controller'; -import { Server } from './server/server'; + PipesConsumer, + PipesContextCreator, +} from '@nestjs/core/internal'; export class MicroservicesModule< TAppOptions extends NestApplicationContextOptions = @@ -48,6 +54,7 @@ export class MicroservicesModule< new GuardsConsumer(), new InterceptorsContextCreator(container, config), new InterceptorsConsumer(), + config, ); const injector = new Injector({ diff --git a/packages/microservices/module/clients.module.ts b/packages/microservices/module/clients.module.ts index 4ed9f2eb93c..42cc91cc3dc 100644 --- a/packages/microservices/module/clients.module.ts +++ b/packages/microservices/module/clients.module.ts @@ -1,18 +1,18 @@ import { - DynamicModule, - ForwardReference, + type DynamicModule, + type ForwardReference, Module, - OnApplicationShutdown, - Provider, - Type, + type OnApplicationShutdown, + type Provider, + type Type, } from '@nestjs/common'; -import { ClientProxy, ClientProxyFactory } from '../client'; +import { ClientProxy, ClientProxyFactory } from '../client/index.js'; import { ClientsModuleAsyncOptions, ClientsModuleOptions, ClientsModuleOptionsFactory, ClientsProviderAsyncOptions, -} from './interfaces'; +} from './interfaces/index.js'; @Module({}) export class ClientsModule { @@ -34,13 +34,10 @@ export class ClientsModule { static registerAsync(options: ClientsModuleAsyncOptions): DynamicModule { const clientsOptions = !Array.isArray(options) ? options.clients : options; - const providers: Provider[] = clientsOptions.reduce( - (accProviders: Provider[], item) => - accProviders - .concat(this.createAsyncProviders(item)) - .concat(item.extraProviders || []), - [], - ); + const providers: Provider[] = clientsOptions.flatMap(item => [ + ...this.createAsyncProviders(item), + ...(item.extraProviders || []), + ]); const imports = clientsOptions.reduce( (accImports, option) => { if (!option.imports) { diff --git a/packages/microservices/module/index.ts b/packages/microservices/module/index.ts index 8a05a28bc30..4e41f31d829 100644 --- a/packages/microservices/module/index.ts +++ b/packages/microservices/module/index.ts @@ -1,2 +1,2 @@ -export * from './clients.module'; -export * from './interfaces'; +export * from './clients.module.js'; +export * from './interfaces/index.js'; diff --git a/packages/microservices/module/interfaces/clients-module.interface.ts b/packages/microservices/module/interfaces/clients-module.interface.ts index 4f932c3143d..d1b9fe47702 100644 --- a/packages/microservices/module/interfaces/clients-module.interface.ts +++ b/packages/microservices/module/interfaces/clients-module.interface.ts @@ -1,5 +1,5 @@ -import { ClientOptions, CustomClientOptions } from '../../interfaces'; -import { Type, Provider, ModuleMetadata } from '@nestjs/common/interfaces'; +import { ClientOptions, CustomClientOptions } from '../../interfaces/index.js'; +import type { Type, Provider, ModuleMetadata } from '@nestjs/common'; export type ClientProvider = ClientOptions | CustomClientOptions; diff --git a/packages/microservices/module/interfaces/index.ts b/packages/microservices/module/interfaces/index.ts index 24943262243..7a61a60f014 100644 --- a/packages/microservices/module/interfaces/index.ts +++ b/packages/microservices/module/interfaces/index.ts @@ -1 +1 @@ -export * from './clients-module.interface'; +export * from './clients-module.interface.js'; diff --git a/packages/microservices/nest-microservice.ts b/packages/microservices/nest-microservice.ts index 2a185b380bf..7d94f2229e6 100644 --- a/packages/microservices/nest-microservice.ts +++ b/packages/microservices/nest-microservice.ts @@ -1,33 +1,29 @@ -import { +import type { CanActivate, ExceptionFilter, INestMicroservice, NestInterceptor, PipeTransform, + PreRequestHook, WebSocketAdapter, } from '@nestjs/common'; -import { NestMicroserviceOptions } from '@nestjs/common/interfaces/microservices/nest-microservice-options.interface'; -import { Logger } from '@nestjs/common/services/logger.service'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { MESSAGES } from '@nestjs/core/constants'; -import { optionalRequire } from '@nestjs/core/helpers/optional-require'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { Injector } from '@nestjs/core/injector/injector'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; -import { NestApplicationContext } from '@nestjs/core/nest-application-context'; -import { Transport } from './enums/transport.enum'; +import { Transport } from './enums/transport.enum.js'; import { AsyncMicroserviceOptions, MicroserviceOptions, -} from './interfaces/microservice-configuration.interface'; -import { MicroservicesModule } from './microservices-module'; -import { Server } from './server/server'; -import { ServerFactory } from './server/server-factory'; - -const { SocketModule } = optionalRequire( - '@nestjs/websockets/socket-module', - () => require('@nestjs/websockets/socket-module'), -); +} from './interfaces/microservice-configuration.interface.js'; +import { MicroservicesModule } from './microservices-module.js'; +import { ServerFactory } from './server/server-factory.js'; +import { Server } from './server/server.js'; +import type { NestMicroserviceOptions } from '@nestjs/common/internal'; +import { Logger } from '@nestjs/common'; +import { + type ApplicationConfig, + type NestContainer, + type GraphInspector, + NestApplicationContext, +} from '@nestjs/core'; +import { MESSAGES, optionalRequire, Injector } from '@nestjs/core/internal'; type CompleteMicroserviceOptions = NestMicroserviceOptions & (MicroserviceOptions | AsyncMicroserviceOptions); @@ -40,7 +36,7 @@ export class NestMicroservice timestamp: true, }); private readonly microservicesModule = new MicroservicesModule(); - private readonly socketModule = SocketModule ? new SocketModule() : null; + private socketModule: any = null; private microserviceConfig: Exclude< CompleteMicroserviceOptions, AsyncMicroserviceOptions @@ -252,10 +248,28 @@ export class NestMicroservice return this; } + /** + * Registers a global preRequest hook (executed before all enhancers for every pattern handler). + * + * @param {...PreRequestHook} hooks + */ + public registerPreRequestHook(...hooks: PreRequestHook[]): this { + if (this.isInitialized) { + this.logger.warn( + 'Cannot apply global preRequest hooks: registration must occur before initialization.', + ); + } + this.applicationConfig.registerPreRequestHook(...hooks); + return this; + } + public async init(): Promise { if (this.isInitialized) { return this; } + + // Lazy-load optional socket module (ESM-compatible) + await this.loadSocketModule(); await super.init(); await this.registerModules(); return this; @@ -378,4 +392,16 @@ export class NestMicroservice } return instances; } + + private async loadSocketModule() { + if (!this.socketModule) { + const socketModule = await optionalRequire( + '@nestjs/websockets/socket-module', + () => import('@nestjs/websockets/socket-module.js'), + ); + if (socketModule?.SocketModule) { + this.socketModule = new socketModule.SocketModule(); + } + } + } } diff --git a/packages/microservices/package.json b/packages/microservices/package.json index 68d6b028aec..3439403cda4 100644 --- a/packages/microservices/package.json +++ b/packages/microservices/package.json @@ -4,6 +4,13 @@ "description": "Nest - modern, fast, powerful node.js web framework (@microservices)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "homepage": "https://nestjs.com", "funding": { "type": "opencollective", @@ -27,6 +34,7 @@ }, "peerDependencies": { "@grpc/grpc-js": "*", + "@nats-io/transport-node": "*", "@nestjs/common": "^11.0.0", "@nestjs/core": "^11.0.0", "@nestjs/websockets": "^11.0.0", @@ -36,7 +44,6 @@ "ioredis": "*", "kafkajs": "*", "mqtt": "*", - "nats": "*", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, @@ -44,6 +51,9 @@ "@grpc/grpc-js": { "optional": true }, + "@nats-io/transport-node": { + "optional": true + }, "@nestjs/websockets": { "optional": true }, @@ -56,9 +66,6 @@ "mqtt": { "optional": true }, - "nats": { - "optional": true - }, "ioredis": { "optional": true }, diff --git a/packages/microservices/record-builders/index.ts b/packages/microservices/record-builders/index.ts index 0bff5e13475..109dc5e10c8 100644 --- a/packages/microservices/record-builders/index.ts +++ b/packages/microservices/record-builders/index.ts @@ -1,3 +1,3 @@ -export * from './mqtt.record-builder'; -export * from './nats.record-builder'; -export * from './rmq.record-builder'; +export * from './mqtt.record-builder.js'; +export * from './nats.record-builder.js'; +export * from './rmq.record-builder.js'; diff --git a/packages/microservices/serializers/identity.serializer.ts b/packages/microservices/serializers/identity.serializer.ts index a210272a0f5..82ca2af3499 100644 --- a/packages/microservices/serializers/identity.serializer.ts +++ b/packages/microservices/serializers/identity.serializer.ts @@ -1,4 +1,4 @@ -import { Serializer } from '../interfaces/serializer.interface'; +import { Serializer } from '../interfaces/serializer.interface.js'; export class IdentitySerializer implements Serializer { serialize(value: any) { diff --git a/packages/microservices/serializers/index.ts b/packages/microservices/serializers/index.ts index b58734cf1b6..5da3ff95c8d 100644 --- a/packages/microservices/serializers/index.ts +++ b/packages/microservices/serializers/index.ts @@ -1,5 +1,5 @@ -export * from './identity.serializer'; -export * from './kafka-request.serializer'; -export * from './mqtt-record.serializer'; -export * from './nats-record.serializer'; -export * from './rmq-record.serializer'; +export * from './identity.serializer.js'; +export * from './kafka-request.serializer.js'; +export * from './mqtt-record.serializer.js'; +export * from './nats-record.serializer.js'; +export * from './rmq-record.serializer.js'; diff --git a/packages/microservices/serializers/kafka-request.serializer.ts b/packages/microservices/serializers/kafka-request.serializer.ts index 8e76e80dfa3..bf13476ad2f 100644 --- a/packages/microservices/serializers/kafka-request.serializer.ts +++ b/packages/microservices/serializers/kafka-request.serializer.ts @@ -1,11 +1,11 @@ +import { Serializer } from '../interfaces/serializer.interface.js'; import { isNil, isObject, isPlainObject, isString, isUndefined, -} from '@nestjs/common/utils/shared.utils'; -import { Serializer } from '../interfaces/serializer.interface'; +} from '@nestjs/common/internal'; export interface KafkaRequest { key: Buffer | string | null; diff --git a/packages/microservices/serializers/mqtt-record.serializer.ts b/packages/microservices/serializers/mqtt-record.serializer.ts index b6797709b54..24ec25c1417 100644 --- a/packages/microservices/serializers/mqtt-record.serializer.ts +++ b/packages/microservices/serializers/mqtt-record.serializer.ts @@ -1,6 +1,6 @@ -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { ReadPacket, Serializer } from '../interfaces'; -import { MqttRecord } from '../record-builders'; +import { ReadPacket, Serializer } from '../interfaces/index.js'; +import { MqttRecord } from '../record-builders/index.js'; +import { isObject } from '@nestjs/common/internal'; export class MqttRecordSerializer implements Serializer { serialize(packet: ReadPacket): string { diff --git a/packages/microservices/serializers/nats-record.serializer.ts b/packages/microservices/serializers/nats-record.serializer.ts index 8162f353668..75b78d43449 100644 --- a/packages/microservices/serializers/nats-record.serializer.ts +++ b/packages/microservices/serializers/nats-record.serializer.ts @@ -1,25 +1,12 @@ -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { NatsCodec } from '../external/nats-codec.interface'; -import { ReadPacket } from '../interfaces'; -import { Serializer } from '../interfaces/serializer.interface'; -import { NatsRecord, NatsRecordBuilder } from '../record-builders'; - -let natsPackage = {} as any; +import { isObject } from '@nestjs/common/internal'; +import { ReadPacket } from '../interfaces/index.js'; +import { Serializer } from '../interfaces/serializer.interface.js'; +import { NatsRecord, NatsRecordBuilder } from '../record-builders/index.js'; export class NatsRecordSerializer implements Serializer< ReadPacket, NatsRecord > { - private readonly jsonCodec: NatsCodec; - - constructor() { - natsPackage = loadPackage('nats', NatsRecordSerializer.name, () => - require('nats'), - ); - this.jsonCodec = natsPackage.JSONCodec(); - } - serialize(packet: any): NatsRecord { const natsMessage = packet?.data && isObject(packet.data) && packet.data instanceof NatsRecord @@ -27,7 +14,7 @@ export class NatsRecordSerializer implements Serializer< : new NatsRecordBuilder(packet?.data).build(); return { - data: this.jsonCodec.encode({ ...packet, data: natsMessage.data }), + data: JSON.stringify({ ...packet, data: natsMessage.data }), headers: natsMessage.headers, }; } diff --git a/packages/microservices/serializers/rmq-record.serializer.ts b/packages/microservices/serializers/rmq-record.serializer.ts index 2846b92377e..1cd701bed4b 100644 --- a/packages/microservices/serializers/rmq-record.serializer.ts +++ b/packages/microservices/serializers/rmq-record.serializer.ts @@ -1,7 +1,7 @@ -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { ReadPacket } from '../interfaces'; -import { Serializer } from '../interfaces/serializer.interface'; -import { RmqRecord } from '../record-builders'; +import { ReadPacket } from '../interfaces/index.js'; +import { Serializer } from '../interfaces/serializer.interface.js'; +import { RmqRecord } from '../record-builders/index.js'; +import { isObject } from '@nestjs/common/internal'; export class RmqRecordSerializer implements Serializer< ReadPacket, diff --git a/packages/microservices/server/index.ts b/packages/microservices/server/index.ts index 078d17a2f58..2ebabdc0b57 100644 --- a/packages/microservices/server/index.ts +++ b/packages/microservices/server/index.ts @@ -1,8 +1,8 @@ -export * from './server'; -export * from './server-grpc'; -export * from './server-kafka'; -export * from './server-mqtt'; -export * from './server-nats'; -export * from './server-redis'; -export * from './server-rmq'; -export * from './server-tcp'; +export * from './server.js'; +export * from './server-grpc.js'; +export * from './server-kafka.js'; +export * from './server-mqtt.js'; +export * from './server-nats.js'; +export * from './server-redis.js'; +export * from './server-rmq.js'; +export * from './server-tcp.js'; diff --git a/packages/microservices/server/server-factory.ts b/packages/microservices/server/server-factory.ts index 35d226f782c..798d38d0c15 100644 --- a/packages/microservices/server/server-factory.ts +++ b/packages/microservices/server/server-factory.ts @@ -1,4 +1,4 @@ -import { Transport } from '../enums/transport.enum'; +import { Transport } from '../enums/transport.enum.js'; import { CustomStrategy, KafkaOptions, @@ -8,14 +8,14 @@ import { RedisOptions, RmqOptions, TcpOptions, -} from '../interfaces'; -import { ServerGrpc } from './server-grpc'; -import { ServerKafka } from './server-kafka'; -import { ServerMqtt } from './server-mqtt'; -import { ServerNats } from './server-nats'; -import { ServerRedis } from './server-redis'; -import { ServerRMQ } from './server-rmq'; -import { ServerTCP } from './server-tcp'; +} from '../interfaces/index.js'; +import { ServerGrpc } from './server-grpc.js'; +import { ServerKafka } from './server-kafka.js'; +import { ServerMqtt } from './server-mqtt.js'; +import { ServerNats } from './server-nats.js'; +import { ServerRedis } from './server-redis.js'; +import { ServerRMQ } from './server-rmq.js'; +import { ServerTCP } from './server-tcp.js'; export class ServerFactory { public static create(microserviceOptions: MicroserviceOptions) { diff --git a/packages/microservices/server/server-grpc.ts b/packages/microservices/server/server-grpc.ts index 426661fb03d..5fc0494551d 100644 --- a/packages/microservices/server/server-grpc.ts +++ b/packages/microservices/server/server-grpc.ts @@ -1,8 +1,4 @@ -import { - isObject, - isString, - isUndefined, -} from '@nestjs/common/utils/shared.utils'; +import { createRequire } from 'module'; import { EMPTY, Observable, @@ -14,19 +10,20 @@ import { lastValueFrom, } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; -import { GRPC_DEFAULT_PROTO_LOADER, GRPC_DEFAULT_URL } from '../constants'; -import { GrpcMethodStreamingType } from '../decorators'; -import { Transport } from '../enums'; -import { InvalidGrpcPackageException } from '../errors/invalid-grpc-package.exception'; -import { InvalidProtoDefinitionException } from '../errors/invalid-proto-definition.exception'; -import { ChannelOptions } from '../external/grpc-options.interface'; -import { getGrpcPackageDefinition } from '../helpers'; -import { MessageHandler } from '../interfaces'; +import { GRPC_DEFAULT_PROTO_LOADER, GRPC_DEFAULT_URL } from '../constants.js'; +import { GrpcMethodStreamingType } from '../decorators/index.js'; +import { Transport } from '../enums/index.js'; +import { InvalidGrpcPackageException } from '../errors/invalid-grpc-package.exception.js'; +import { InvalidProtoDefinitionException } from '../errors/invalid-proto-definition.exception.js'; +import { ChannelOptions } from '../external/grpc-options.interface.js'; +import { getGrpcPackageDefinition } from '../helpers/index.js'; +import { MessageHandler } from '../interfaces/index.js'; import { GrpcOptions, TransportId, -} from '../interfaces/microservice-configuration.interface'; -import { Server } from './server'; +} from '../interfaces/microservice-configuration.interface.js'; +import { Server } from './server.js'; +import { isObject, isString, isUndefined } from '@nestjs/common/internal'; const CANCELLED_EVENT = 'cancelled'; @@ -74,16 +71,14 @@ export class ServerGrpc extends Server { const protoLoader = this.getOptionsProp(options, 'protoLoader') || GRPC_DEFAULT_PROTO_LOADER; - grpcPackage = this.loadPackage('@grpc/grpc-js', ServerGrpc.name, () => - require('@grpc/grpc-js'), + grpcPackage = this.loadPackageSynchronously( + '@grpc/grpc-js', + ServerGrpc.name, + () => createRequire(import.meta.url)('@grpc/grpc-js'), ); - grpcProtoLoaderPackage = this.loadPackage( + grpcProtoLoaderPackage = this.loadPackageSynchronously( protoLoader, ServerGrpc.name, - () => - protoLoader === GRPC_DEFAULT_PROTO_LOADER - ? require('@grpc/proto-loader') - : require(protoLoader), ); } @@ -168,8 +163,8 @@ export class ServerGrpc extends Server { const service = {}; for (const methodName in grpcService.prototype) { - let methodHandler: MessageHandler | null = null; - let streamingType = GrpcMethodStreamingType.NO_STREAMING; + let methodHandler: MessageHandler | null; + let streamingType: GrpcMethodStreamingType; const methodFunction = grpcService.prototype[methodName]; const methodReqStreaming = methodFunction.requestStream; @@ -710,7 +705,7 @@ export class ServerGrpc extends Server { return key; } // Otherwise add next through dot syntax - return name + '.' + key; + return `${name}.${key}`; } private async createServices(grpcPkg: any, packageName: string) { diff --git a/packages/microservices/server/server-kafka.ts b/packages/microservices/server/server-kafka.ts index 939096e6443..174c263656d 100644 --- a/packages/microservices/server/server-kafka.ts +++ b/packages/microservices/server/server-kafka.ts @@ -1,5 +1,3 @@ -import { Logger } from '@nestjs/common/services/logger.service'; -import { isNil } from '@nestjs/common/utils/shared.utils'; import { isObservable, lastValueFrom, Observable, ReplaySubject } from 'rxjs'; import { KAFKA_DEFAULT_BROKER, @@ -7,12 +5,12 @@ import { KAFKA_DEFAULT_GROUP, NO_EVENT_HANDLER, NO_MESSAGE_HANDLER, -} from '../constants'; -import { KafkaContext } from '../ctx-host'; -import { KafkaRequestDeserializer } from '../deserializers/kafka-request.deserializer'; -import { KafkaHeaders, Transport } from '../enums'; -import { KafkaStatus } from '../events'; -import { KafkaRetriableException } from '../exceptions'; +} from '../constants.js'; +import { KafkaContext } from '../ctx-host/index.js'; +import { KafkaRequestDeserializer } from '../deserializers/kafka-request.deserializer.js'; +import { KafkaHeaders, Transport } from '../enums/index.js'; +import { KafkaStatus } from '../events/index.js'; +import { KafkaRetriableException } from '../exceptions/index.js'; import { BrokersFunction, Consumer, @@ -24,18 +22,18 @@ import { Message, Producer, RecordMetadata, -} from '../external/kafka.interface'; -import { KafkaLogger, KafkaParser } from '../helpers'; +} from '../external/kafka.interface.js'; +import { KafkaLogger, KafkaParser } from '../helpers/index.js'; import { KafkaOptions, OutgoingResponse, ReadPacket, TransportId, -} from '../interfaces'; -import { KafkaRequestSerializer } from '../serializers/kafka-request.serializer'; -import { Server } from './server'; - -let kafkaPackage: any = {}; +} from '../interfaces/index.js'; +import { KafkaRequestSerializer } from '../serializers/kafka-request.serializer.js'; +import { Server } from './server.js'; +import { Logger } from '@nestjs/common'; +import { isNil } from '@nestjs/common/internal'; /** * @publicApi @@ -75,10 +73,6 @@ export class ServerKafka extends Server { (clientOptions.clientId || KAFKA_DEFAULT_CLIENT) + postfixId; this.groupId = (consumerOptions.groupId || KAFKA_DEFAULT_GROUP) + postfixId; - kafkaPackage = this.loadPackage('kafkajs', ServerKafka.name, () => - require('kafkajs'), - ); - this.parser = new KafkaParser((options && options.parser) || undefined); this.initializeSerializer(options); @@ -89,7 +83,7 @@ export class ServerKafka extends Server { callback: (err?: unknown, ...optionalParams: unknown[]) => void, ): Promise { try { - this.client = this.createClient(); + this.client = await this.createClient(); await this.start(callback); } catch (err) { callback(err); @@ -105,9 +99,10 @@ export class ServerKafka extends Server { } public async start(callback: () => void): Promise { - const consumerOptions = Object.assign(this.options.consumer || {}, { + const consumerOptions = { + ...(this.options.consumer || {}), groupId: this.groupId, - }); + }; this.consumer = this.client!.consumer(consumerOptions); this.producer = this.client!.producer(this.options.producer); this.registerConsumerEventListeners(); @@ -152,14 +147,18 @@ export class ServerKafka extends Server { ); } - public createClient(): T { - return new kafkaPackage.Kafka( - Object.assign( - { logCreator: KafkaLogger.bind(null, this.logger) }, - this.options.client, - { clientId: this.clientId, brokers: this.brokers }, - ) as KafkaConfig, + public async createClient(): Promise { + const kafkaPackage = await this.loadPackage( + 'kafkajs', + ServerKafka.name, + () => import('kafkajs'), ); + return new kafkaPackage.Kafka({ + logCreator: KafkaLogger.bind(null, this.logger), + ...this.options.client, + clientId: this.clientId, + brokers: this.brokers, + } as KafkaConfig) as T; } public async bindEvents(consumer: Consumer) { @@ -173,9 +172,10 @@ export class ServerKafka extends Server { }); } - const consumerRunOptions = Object.assign(this.options.run || {}, { + const consumerRunOptions = { + ...(this.options.run || {}), eachMessage: this.getMessageHandler(), - }); + }; await consumer.run(consumerRunOptions); } @@ -314,13 +314,11 @@ export class ServerKafka extends Server { this.assignErrorHeader(message, outgoingMessage); this.assignIsDisposedHeader(message, outgoingMessage); - const replyMessage = Object.assign( - { - topic: replyTopic, - messages: [outgoingMessage], - }, - this.options.send || {}, - ); + const replyMessage = { + topic: replyTopic, + messages: [outgoingMessage], + ...(this.options.send || {}), + }; return this.producer!.send(replyMessage).finally(() => { this.onProcessingEndHook?.(this.transportId, context); }); diff --git a/packages/microservices/server/server-mqtt.ts b/packages/microservices/server/server-mqtt.ts index 37950bd753c..1daa596d78f 100644 --- a/packages/microservices/server/server-mqtt.ts +++ b/packages/microservices/server/server-mqtt.ts @@ -1,29 +1,31 @@ -import { isObject, isUndefined } from '@nestjs/common/utils/shared.utils'; import { MQTT_DEFAULT_URL, MQTT_SEPARATOR, MQTT_WILDCARD_ALL, MQTT_WILDCARD_SINGLE, NO_MESSAGE_HANDLER, -} from '../constants'; -import { MqttContext } from '../ctx-host/mqtt.context'; -import { Transport } from '../enums'; -import { MqttEvents, MqttEventsMap, MqttStatus } from '../events/mqtt.events'; +} from '../constants.js'; +import { MqttContext } from '../ctx-host/mqtt.context.js'; +import { Transport } from '../enums/index.js'; +import { + MqttEvents, + MqttEventsMap, + MqttStatus, +} from '../events/mqtt.events.js'; import { IncomingRequest, MessageHandler, PacketId, ReadPacket, -} from '../interfaces'; +} from '../interfaces/index.js'; import { MqttOptions, TransportId, -} from '../interfaces/microservice-configuration.interface'; -import { MqttRecord } from '../record-builders/mqtt.record-builder'; -import { MqttRecordSerializer } from '../serializers/mqtt-record.serializer'; -import { Server } from './server'; - -let mqttPackage: any = {}; +} from '../interfaces/microservice-configuration.interface.js'; +import { MqttRecord } from '../record-builders/mqtt.record-builder.js'; +import { MqttRecordSerializer } from '../serializers/mqtt-record.serializer.js'; +import { Server } from './server.js'; +import { isObject, isUndefined } from '@nestjs/common/internal'; // To enable type safety for MQTT. This cant be uncommented by default // because it would require the user to install the mqtt package even if they dont use MQTT @@ -48,10 +50,6 @@ export class ServerMqtt extends Server { super(); this.url = this.getOptionsProp(options, 'url', MQTT_DEFAULT_URL); - mqttPackage = this.loadPackage('mqtt', ServerMqtt.name, () => - require('mqtt'), - ); - this.initializeSerializer(options); this.initializeDeserializer(options); } @@ -60,7 +58,7 @@ export class ServerMqtt extends Server { callback: (err?: unknown, ...optionalParams: unknown[]) => void, ) { try { - this.mqttClient = this.createMqttClient(); + this.mqttClient = await this.createMqttClient(); this.start(callback); } catch (err) { callback(err); @@ -114,8 +112,13 @@ export class ServerMqtt extends Server { this.pendingEventListeners = []; } - public createMqttClient(): MqttClient { - return mqttPackage.connect(this.url, this.options as MqttOptions); + public async createMqttClient(): Promise { + const mqttPackage = await this.loadPackage( + 'mqtt', + ServerMqtt.name, + () => import('mqtt'), + ); + return mqttPackage.connect(this.url, this.options as any); } public getMessageHandler(pub: MqttClient) { diff --git a/packages/microservices/server/server-nats.ts b/packages/microservices/server/server-nats.ts index bc08517b069..2704139b712 100644 --- a/packages/microservices/server/server-nats.ts +++ b/packages/microservices/server/server-nats.ts @@ -1,32 +1,34 @@ -import { isObject, isUndefined } from '@nestjs/common/utils/shared.utils'; +import { isObject, isUndefined } from '@nestjs/common/internal'; import { EventEmitter } from 'events'; import { NATS_DEFAULT_GRACE_PERIOD, NATS_DEFAULT_URL, NO_MESSAGE_HANDLER, -} from '../constants'; -import { NatsContext } from '../ctx-host/nats.context'; -import { NatsRequestJSONDeserializer } from '../deserializers/nats-request-json.deserializer'; -import { Transport } from '../enums'; -import { NatsEvents, NatsEventsMap, NatsStatus } from '../events/nats.events'; +} from '../constants.js'; +import { NatsContext } from '../ctx-host/nats.context.js'; +import { NatsRequestJSONDeserializer } from '../deserializers/nats-request-json.deserializer.js'; +import { Transport } from '../enums/index.js'; +import { + NatsEvents, + NatsEventsMap, + NatsStatus, +} from '../events/nats.events.js'; import { NatsOptions, TransportId, -} from '../interfaces/microservice-configuration.interface'; -import { IncomingRequest } from '../interfaces/packet.interface'; -import { NatsRecord } from '../record-builders'; -import { NatsRecordSerializer } from '../serializers/nats-record.serializer'; -import { Server } from './server'; - -let natsPackage = {} as any; +} from '../interfaces/microservice-configuration.interface.js'; +import { IncomingRequest } from '../interfaces/packet.interface.js'; +import { NatsRecord } from '../record-builders/index.js'; +import { NatsRecordSerializer } from '../serializers/nats-record.serializer.js'; +import { Server } from './server.js'; // To enable type safety for Nats. This cant be uncommented by default // because it would require the user to install the nats package even if they dont use Nats // Otherwise, TypeScript would fail to compile the code. // -// type Client = import('nats').NatsConnection; -// type NatsMsg = import('nats').Msg; -// type Subscription = import('nats').Subscription; +// type Client = import('@nats-io/transport-node').NatsConnection; +// type NatsMsg = import('@nats-io/transport-node').Msg; +// type Subscription = import('@nats-io/transport-node').Subscription; type Client = any; type NatsMsg = any; @@ -50,10 +52,6 @@ export class ServerNats< constructor(private readonly options: Required['options']) { super(); - natsPackage = this.loadPackage('nats', ServerNats.name, () => - require('nats'), - ); - this.initializeSerializer(options); this.initializeDeserializer(options); } @@ -123,7 +121,13 @@ export class ServerNats< this.natsClient = null; } - public createNatsClient(): Promise { + public async createNatsClient(): Promise { + const natsPackage = await this.loadPackage( + '@nats-io/transport-node', + ServerNats.name, + () => import('@nats-io/transport-node'), + ); + const options = this.options || ({} as NatsOptions); return natsPackage.connect({ servers: NATS_DEFAULT_URL, @@ -146,7 +150,7 @@ export class ServerNats< const replyTo = natsMsg.reply; const natsCtx = new NatsContext([callerSubject, natsMsg.headers]); - const message = await this.deserializer.deserialize(rawMessage, { + const message = await this.deserializer.deserialize(natsMsg, { channel, replyTo, }); @@ -198,34 +202,24 @@ export class ServerNats< public async handleStatusUpdates(client: Client) { for await (const status of client.status()) { - const data = - status.data && isObject(status.data) - ? JSON.stringify(status.data) - : status.data; - switch (status.type) { case 'error': this.logger.error( - `NatsError: type: "${status.type}", data: "${data}".`, + `NatsError: type: "${status.type}", error: "${status.error}".`, ); break; case 'disconnect': - this.logger.error( - `NatsError: type: "${status.type}", data: "${data}".`, - ); + this.logger.error(`NatsError: type: "${status.type}".`); this._status$.next(NatsStatus.DISCONNECTED as S); - this.statusEventEmitter.emit( - NatsEventsMap.DISCONNECT, - status.data as string, - ); + this.statusEventEmitter.emit(NatsEventsMap.DISCONNECT, status.server); break; - case 'pingTimer': + case 'ping': if (this.options.debug) { this.logger.debug!( - `NatsStatus: type: "${status.type}", data: "${data}".`, + `NatsStatus: type: "${status.type}", pending pings: "${status.pendingPings}".`, ); } break; @@ -235,29 +229,31 @@ export class ServerNats< break; case 'reconnect': - this.logger.log( - `NatsStatus: type: "${status.type}", data: "${data}".`, - ); + this.logger.log(`NatsStatus: type: "${status.type}".`); this._status$.next(NatsStatus.CONNECTED as S); - this.statusEventEmitter.emit( - NatsEventsMap.RECONNECT, - status.data as string, - ); + this.statusEventEmitter.emit(NatsEventsMap.RECONNECT, status.server); break; case 'update': this.logger.log( - `NatsStatus: type: "${status.type}", data: "${data}".`, + `NatsStatus: type: "${status.type}", added: "${status.added}", deleted: "${status.deleted}".`, ); - this.statusEventEmitter.emit(NatsEventsMap.UPDATE, status.data); + this.statusEventEmitter.emit(NatsEventsMap.UPDATE, undefined); break; - default: + default: { + const data = + 'data' in status && isObject(status.data) + ? JSON.stringify(status.data) + : 'data' in status + ? status.data + : ''; this.logger.log( `NatsStatus: type: "${status.type}", data: "${data}".`, ); break; + } } } } diff --git a/packages/microservices/server/server-redis.ts b/packages/microservices/server/server-redis.ts index 10bf9c1cd1f..90867fa1fb5 100644 --- a/packages/microservices/server/server-redis.ts +++ b/packages/microservices/server/server-redis.ts @@ -1,18 +1,22 @@ -import { isUndefined } from '@nestjs/common/utils/shared.utils'; import { NO_MESSAGE_HANDLER, REDIS_DEFAULT_HOST, REDIS_DEFAULT_PORT, -} from '../constants'; -import { RedisContext } from '../ctx-host'; -import { Transport } from '../enums'; +} from '../constants.js'; +import { RedisContext } from '../ctx-host/index.js'; +import { Transport } from '../enums/index.js'; import { RedisEvents, RedisEventsMap, RedisStatus, -} from '../events/redis.events'; -import { IncomingRequest, RedisOptions, TransportId } from '../interfaces'; -import { Server } from './server'; +} from '../events/redis.events.js'; +import { + IncomingRequest, + RedisOptions, + TransportId, +} from '../interfaces/index.js'; +import { Server } from './server.js'; +import { isUndefined } from '@nestjs/common/internal'; // To enable type safety for Redis. This cant be uncommented by default // because it would require the user to install the ioredis package even if they dont use Redis @@ -21,8 +25,6 @@ import { Server } from './server'; // type Redis = import('ioredis').Redis; type Redis = any; -let redisPackage = {} as any; - /** * @publicApi */ @@ -41,20 +43,16 @@ export class ServerRedis extends Server { constructor(protected readonly options: Required['options']) { super(); - redisPackage = this.loadPackage('ioredis', ServerRedis.name, () => - require('ioredis'), - ); - this.initializeSerializer(options); this.initializeDeserializer(options); } - public listen( + public async listen( callback: (err?: unknown, ...optionalParams: unknown[]) => void, ) { try { - this.subClient = this.createRedisClient(); - this.pubClient = this.createRedisClient(); + this.subClient = await this.createRedisClient(); + this.pubClient = await this.createRedisClient(); [this.subClient, this.pubClient].forEach((client, index) => { const type = index === 0 ? 'pub' : 'sub'; @@ -111,8 +109,14 @@ export class ServerRedis extends Server { this.pendingEventListeners = []; } - public createRedisClient(): Redis { - return new redisPackage({ + public async createRedisClient(): Promise { + const redisPackage = await this.loadPackage( + 'ioredis', + ServerRedis.name, + () => import('ioredis'), + ); + const RedisClient: any = redisPackage.default || redisPackage; + return new RedisClient({ port: REDIS_DEFAULT_PORT, host: REDIS_DEFAULT_HOST, ...this.getClientOptions(), diff --git a/packages/microservices/server/server-rmq.ts b/packages/microservices/server/server-rmq.ts index 102c6a81a0b..0572ca0493b 100644 --- a/packages/microservices/server/server-rmq.ts +++ b/packages/microservices/server/server-rmq.ts @@ -1,9 +1,5 @@ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */ -import { - isNil, - isString, - isUndefined, -} from '@nestjs/common/utils/shared.utils'; +import { createRequire } from 'module'; import { CONNECTION_FAILED_MESSAGE, DISCONNECTED_RMQ_MESSAGE, @@ -11,28 +7,33 @@ import { RMQ_SEPARATOR, RMQ_WILDCARD_ALL, RMQ_WILDCARD_SINGLE, - RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT, - RQM_DEFAULT_NOACK, - RQM_DEFAULT_NO_ASSERT, - RQM_DEFAULT_PREFETCH_COUNT, - RQM_DEFAULT_QUEUE, - RQM_DEFAULT_QUEUE_OPTIONS, - RQM_DEFAULT_URL, - RQM_NO_EVENT_HANDLER, - RQM_NO_MESSAGE_HANDLER, -} from '../constants'; -import { RmqContext } from '../ctx-host'; -import { Transport } from '../enums'; -import { RmqEvents, RmqEventsMap, RmqStatus } from '../events/rmq.events'; -import { RmqUrl } from '../external/rmq-url.interface'; -import { MessageHandler, RmqOptions, TransportId } from '../interfaces'; + RMQ_DEFAULT_IS_GLOBAL_PREFETCH_COUNT, + RMQ_DEFAULT_NOACK, + RMQ_DEFAULT_NO_ASSERT, + RMQ_DEFAULT_PREFETCH_COUNT, + RMQ_DEFAULT_QUEUE, + RMQ_DEFAULT_QUEUE_OPTIONS, + RMQ_DEFAULT_URL, + RMQ_NO_EVENT_HANDLER, + RMQ_NO_MESSAGE_HANDLER, +} from '../constants.js'; +import { RmqContext } from '../ctx-host/index.js'; +import { Transport } from '../enums/index.js'; +import { RmqEvents, RmqEventsMap, RmqStatus } from '../events/rmq.events.js'; +import { RmqUrl } from '../external/rmq-url.interface.js'; +import { + MessageHandler, + RmqOptions, + TransportId, +} from '../interfaces/index.js'; import { IncomingRequest, OutgoingResponse, ReadPacket, -} from '../interfaces/packet.interface'; -import { RmqRecordSerializer } from '../serializers/rmq-record.serializer'; -import { Server } from './server'; +} from '../interfaces/packet.interface.js'; +import { RmqRecordSerializer } from '../serializers/rmq-record.serializer.js'; +import { Server } from './server.js'; +import { isNil, isString, isUndefined } from '@nestjs/common/internal'; // To enable type safety for RMQ. This cant be uncommented by default // because it would require the user to install the amqplib package even if they dont use RabbitMQ @@ -49,8 +50,6 @@ type ChannelWrapper = any; type Message = any; type Channel = any; -let rmqPackage = {} as any; // as typeof import('amqp-connection-manager'); - const INFINITE_CONNECTION_ATTEMPTS = -1; /** @@ -74,19 +73,16 @@ export class ServerRMQ extends Server { constructor(protected readonly options: Required['options']) { super(); - this.urls = this.getOptionsProp(this.options, 'urls') || [RQM_DEFAULT_URL]; + this.urls = this.getOptionsProp(this.options, 'urls') || [RMQ_DEFAULT_URL]; this.queue = - this.getOptionsProp(this.options, 'queue') || RQM_DEFAULT_QUEUE; - this.noAck = this.getOptionsProp(this.options, 'noAck', RQM_DEFAULT_NOACK); + this.getOptionsProp(this.options, 'queue') || RMQ_DEFAULT_QUEUE; + this.noAck = this.getOptionsProp(this.options, 'noAck', RMQ_DEFAULT_NOACK); this.queueOptions = this.getOptionsProp(this.options, 'queueOptions') || - RQM_DEFAULT_QUEUE_OPTIONS; + RMQ_DEFAULT_QUEUE_OPTIONS; - this.loadPackage('amqplib', ServerRMQ.name, () => require('amqplib')); - rmqPackage = this.loadPackage( - 'amqp-connection-manager', - ServerRMQ.name, - () => require('amqp-connection-manager'), + this.loadPackageSynchronously('amqplib', ServerRMQ.name, () => + createRequire(import.meta.url)('amqplib'), ); this.initializeSerializer(options); @@ -112,7 +108,7 @@ export class ServerRMQ extends Server { public async start( callback?: (err?: unknown, ...optionalParams: unknown[]) => void, ) { - this.server = this.createClient(); + this.server = await this.createClient(); this.server!.once(RmqEventsMap.CONNECT, () => { if (this.channel) { return; @@ -162,13 +158,18 @@ export class ServerRMQ extends Server { ); } - public createClient(): T { + public async createClient(): Promise { + const rmqPackage = await this.loadPackage( + 'amqp-connection-manager', + ServerRMQ.name, + () => import('amqp-connection-manager'), + ); const socketOptions = this.getOptionsProp(this.options, 'socketOptions'); return rmqPackage.connect(this.urls, { connectionOptions: socketOptions?.connectionOptions, heartbeatIntervalInSeconds: socketOptions?.heartbeatIntervalInSeconds, reconnectTimeInSeconds: socketOptions?.reconnectTimeInSeconds, - }); + }) as T; } private registerConnectListener() { @@ -189,11 +190,11 @@ export class ServerRMQ extends Server { const noAssert = this.getOptionsProp(this.options, 'noAssert') ?? this.queueOptions.noAssert ?? - RQM_DEFAULT_NO_ASSERT; + RMQ_DEFAULT_NO_ASSERT; let createdQueue: string; - if (this.queue === RQM_DEFAULT_QUEUE || !noAssert) { + if (this.queue === RMQ_DEFAULT_QUEUE || !noAssert) { const { queue } = await channel.assertQueue( this.queue, this.queueOptions, @@ -206,12 +207,12 @@ export class ServerRMQ extends Server { const isGlobalPrefetchCount = this.getOptionsProp( this.options, 'isGlobalPrefetchCount', - RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT, + RMQ_DEFAULT_IS_GLOBAL_PREFETCH_COUNT, ); const prefetchCount = this.getOptionsProp( this.options, 'prefetchCount', - RQM_DEFAULT_PREFETCH_COUNT, + RMQ_DEFAULT_PREFETCH_COUNT, ); if (this.options.exchange || this.options.wildcards) { @@ -291,7 +292,7 @@ export class ServerRMQ extends Server { if (!handler) { if (!this.noAck) { - this.logger.warn(RQM_NO_MESSAGE_HANDLER`${pattern}`); + this.logger.warn(RMQ_NO_MESSAGE_HANDLER`${pattern}`); this.channel!.nack(rmqContext.getMessage() as Message, false, false); } const status = 'error'; @@ -336,7 +337,7 @@ export class ServerRMQ extends Server { const handler = this.getHandlerByPattern(pattern); if (!handler && !this.noAck) { this.channel!.nack(context.getMessage() as Message, false, false); - return this.logger.warn(RQM_NO_EVENT_HANDLER`${pattern}`); + return this.logger.warn(RMQ_NO_EVENT_HANDLER`${pattern}`); } return super.handleEvent(pattern, packet, context); } diff --git a/packages/microservices/server/server-tcp.ts b/packages/microservices/server/server-tcp.ts index eed4fc75a6d..b7d18e97643 100644 --- a/packages/microservices/server/server-tcp.ts +++ b/packages/microservices/server/server-tcp.ts @@ -1,5 +1,4 @@ -import { Type } from '@nestjs/common'; -import { isString, isUndefined } from '@nestjs/common/utils/shared.utils'; +import type { Type } from '@nestjs/common'; import * as net from 'net'; import { Server as NetSocket, Socket } from 'net'; import { createServer as tlsCreateServer, TlsOptions } from 'tls'; @@ -9,23 +8,24 @@ import { NO_MESSAGE_HANDLER, TCP_DEFAULT_HOST, TCP_DEFAULT_PORT, -} from '../constants'; -import { TcpContext } from '../ctx-host/tcp.context'; -import { Transport } from '../enums'; -import { TcpEvents, TcpEventsMap, TcpStatus } from '../events/tcp.events'; -import { JsonSocket, TcpSocket } from '../helpers'; -import { InvalidTcpDataReceptionException } from '../errors/invalid-tcp-data-reception.exception'; +} from '../constants.js'; +import { TcpContext } from '../ctx-host/tcp.context.js'; +import { Transport } from '../enums/index.js'; +import { TcpEvents, TcpEventsMap, TcpStatus } from '../events/tcp.events.js'; +import { JsonSocket, TcpSocket } from '../helpers/index.js'; +import { InvalidTcpDataReceptionException } from '../errors/invalid-tcp-data-reception.exception.js'; import { IncomingRequest, PacketId, ReadPacket, WritePacket, -} from '../interfaces'; +} from '../interfaces/index.js'; import { TcpOptions, TransportId, -} from '../interfaces/microservice-configuration.interface'; -import { Server } from './server'; +} from '../interfaces/microservice-configuration.interface.js'; +import { Server } from './server.js'; +import { isString, isUndefined } from '@nestjs/common/internal'; /** * @publicApi diff --git a/packages/microservices/server/server.ts b/packages/microservices/server/server.ts index 10b9e72445b..d035b5cdb22 100644 --- a/packages/microservices/server/server.ts +++ b/packages/microservices/server/server.ts @@ -1,5 +1,3 @@ -import { Logger, LoggerService } from '@nestjs/common/services/logger.service'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; import { connectable, EMPTY, @@ -18,10 +16,11 @@ import { finalize, mergeMap, } from 'rxjs/operators'; -import { NO_EVENT_HANDLER } from '../constants'; -import { BaseRpcContext } from '../ctx-host/base-rpc.context'; -import { IncomingRequestDeserializer } from '../deserializers/incoming-request.deserializer'; -import { Transport } from '../enums'; +import { NO_EVENT_HANDLER } from '../constants.js'; +import { BaseRpcContext } from '../ctx-host/base-rpc.context.js'; +import { IncomingRequestDeserializer } from '../deserializers/incoming-request.deserializer.js'; +import { Transport } from '../enums/index.js'; +import { ConsumerDeserializer } from '../interfaces/deserializer.interface.js'; import { ClientOptions, KafkaOptions, @@ -35,11 +34,12 @@ import { RmqOptions, TcpOptions, WritePacket, -} from '../interfaces'; -import { ConsumerDeserializer } from '../interfaces/deserializer.interface'; -import { ConsumerSerializer } from '../interfaces/serializer.interface'; -import { IdentitySerializer } from '../serializers/identity.serializer'; -import { transformPatternToRoute } from '../utils'; +} from '../interfaces/index.js'; +import { ConsumerSerializer } from '../interfaces/serializer.interface.js'; +import { IdentitySerializer } from '../serializers/identity.serializer.js'; +import { transformPatternToRoute } from '../utils/index.js'; +import { Logger, type LoggerService } from '@nestjs/common'; +import { loadPackage, loadPackageSync } from '@nestjs/common/internal'; /** * @publicApi @@ -289,11 +289,19 @@ export abstract class Server< protected loadPackage( name: string, ctx: string, - loader?: Function, - ): T { + loader?: () => T, + ): T | Promise { return loadPackage(name, ctx, loader); } + protected loadPackageSynchronously( + name: string, + ctx: string, + loader?: () => T, + ): T { + return loadPackageSync(name, ctx, loader); + } + protected initializeSerializer(options: ClientOptions['options']) { this.serializer = (options && diff --git a/packages/microservices/test/client/client-grpc.spec.ts b/packages/microservices/test/client/client-grpc.spec.ts index bb91f8479e2..22f1734853b 100644 --- a/packages/microservices/test/client/client-grpc.spec.ts +++ b/packages/microservices/test/client/client-grpc.spec.ts @@ -1,13 +1,10 @@ import { Logger } from '@nestjs/common'; -import { expect } from 'chai'; import { join } from 'path'; import { Observable, Subject } from 'rxjs'; -import * as sinon from 'sinon'; -import { ClientGrpcProxy } from '../../client'; -import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception'; -import { InvalidGrpcServiceException } from '../../errors/invalid-grpc-service.exception'; -import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception'; -import * as grpcHelpers from '../../helpers/grpc-helpers'; +import { ClientGrpcProxy } from '../../client/index.js'; +import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception.js'; +import { InvalidGrpcServiceException } from '../../errors/invalid-grpc-service.exception.js'; +import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception.js'; class NoopLogger extends Logger { log(message: any, context?: string): void {} @@ -27,7 +24,7 @@ describe('ClientGrpcProxy', () => { beforeEach(() => { client = new ClientGrpcProxy({ - protoPath: join(__dirname, './test.proto'), + protoPath: join(import.meta.dirname, './test.proto'), package: 'test', }); untypedClient = client as any; @@ -36,7 +33,7 @@ describe('ClientGrpcProxy', () => { protoPath: ['test.proto', 'test2.proto'], package: ['test', 'test2'], loader: { - includeDirs: [join(__dirname, '.')], + includeDirs: [join(import.meta.dirname, '.')], }, }); }); @@ -45,7 +42,7 @@ describe('ClientGrpcProxy', () => { describe('when "grpcClient[name]" is nil', () => { it('should throw "InvalidGrpcServiceException"', () => { untypedClient.grpcClient = {}; - expect(() => client.getService('test')).to.throw( + expect(() => client.getService('test')).toThrow( InvalidGrpcServiceException, ); }); @@ -53,37 +50,31 @@ describe('ClientGrpcProxy', () => { it('should throw "InvalidGrpcServiceException" (multiple proto)', () => { (clientMulti as any).grpcClient = {}; - expect(() => clientMulti.getService('test')).to.throw( + expect(() => clientMulti.getService('test')).toThrow( InvalidGrpcServiceException, ); - expect(() => clientMulti.getService('test2')).to.throw( + expect(() => clientMulti.getService('test2')).toThrow( InvalidGrpcServiceException, ); }); }); describe('when "grpcClient[name]" is not nil', () => { - it('should create grpcService', () => { + it('should create grpcService', async () => { untypedClient.grpcClients[0] = { test: GrpcService, }; - expect(() => client.getService('test')).to.not.throw( - InvalidGrpcServiceException, - ); + client.getService('test'); // should not throw }); describe('when "grpcClient[name]" is not nil (multiple proto)', () => { - it('should create grpcService', () => { + it('should create grpcService', async () => { (clientMulti as any).grpcClients[0] = { test: GrpcService, test2: GrpcService, }; - expect(() => clientMulti.getService('test')).to.not.throw( - InvalidGrpcServiceException, - ); - expect(() => clientMulti.getService('test2')).to.not.throw( - InvalidGrpcServiceException, - ); + clientMulti.getService('test'); // should not throw + clientMulti.getService('test2'); // should not throw }); }); }); @@ -94,19 +85,19 @@ describe('ClientGrpcProxy', () => { describe('when method is a response stream', () => { it('should call "createStreamServiceMethod"', () => { const cln = { [methodName]: { responseStream: true } }; - const spy = sinon.spy(client, 'createStreamServiceMethod'); + const spy = vi.spyOn(client, 'createStreamServiceMethod'); client.createServiceMethod(cln, methodName); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); describe('when method is not a response stream', () => { it('should call "createUnaryServiceMethod"', () => { const cln = { [methodName]: { responseStream: false } }; - const spy = sinon.spy(client, 'createUnaryServiceMethod'); + const spy = vi.spyOn(client, 'createUnaryServiceMethod'); client.createServiceMethod(cln, methodName); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); }); @@ -118,7 +109,7 @@ describe('ClientGrpcProxy', () => { { [methodKey]: {} }, methodKey, ); - expect(fn()).to.be.instanceof(Observable); + expect(fn()).toBeInstanceOf(Observable); }); describe('on subscribe', () => { const methodName = 'm'; @@ -131,19 +122,19 @@ describe('ClientGrpcProxy', () => { }); it('should call native method', () => { - const spy = sinon.spy(obj, methodName); + const spy = vi.spyOn(obj, methodName); stream$.subscribe({ next: () => ({}), error: () => ({}), }); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); describe('when stream request', () => { const methodName = 'm'; - const writeSpy = sinon.spy(); + const writeSpy = vi.fn(); const obj = { [methodName]: () => ({ on: (type, fn) => fn(), write: writeSpy }), }; @@ -158,15 +149,15 @@ describe('ClientGrpcProxy', () => { }); it('should subscribe to request upstream', () => { - const upstreamSubscribe = sinon.spy(upstream, 'subscribe'); + const upstreamSubscribe = vi.spyOn(upstream, 'subscribe'); stream$.subscribe({ next: () => ({}), error: () => ({}), }); upstream.next({ test: true }); - expect(writeSpy.called).to.be.true; - expect(upstreamSubscribe.called).to.be.true; + expect(writeSpy).toHaveBeenCalled(); + expect(upstreamSubscribe).toHaveBeenCalled(); }); }); @@ -175,10 +166,10 @@ describe('ClientGrpcProxy', () => { type EvtCallback = (...args: any[]) => void; let callMock: { on: (type: string, fn: EvtCallback) => void; - cancel: sinon.SinonSpy; + cancel: ReturnType; finished: boolean; - destroy: sinon.SinonSpy; - removeAllListeners: sinon.SinonSpy; + destroy: ReturnType; + removeAllListeners: ReturnType; }; let eventCallbacks: { [type: string]: EvtCallback }; let obj, dataSpy, errorSpy, completeSpy; @@ -186,17 +177,17 @@ describe('ClientGrpcProxy', () => { let stream$: Observable; beforeEach(() => { - dataSpy = sinon.spy(); - errorSpy = sinon.spy(); - completeSpy = sinon.spy(); + dataSpy = vi.fn(); + errorSpy = vi.fn(); + completeSpy = vi.fn(); eventCallbacks = {}; callMock = { on: (type, fn) => (eventCallbacks[type] = fn), - cancel: sinon.spy(), + cancel: vi.fn(), finished: false, - destroy: sinon.spy(), - removeAllListeners: sinon.spy(), + destroy: vi.fn(), + removeAllListeners: vi.fn(), }; obj = { [methodName]: () => callMock }; stream$ = client.createStreamServiceMethod(obj, methodName)(); @@ -216,11 +207,11 @@ describe('ClientGrpcProxy', () => { eventCallbacks.error(err); eventCallbacks.data('c'); - expect(Object.keys(eventCallbacks).length).to.eq(3); - expect(dataSpy.args).to.eql([['a'], ['b']]); - expect(errorSpy.args[0][0]).to.eql(err); - expect(completeSpy.called).to.be.false; - expect(callMock.cancel.called).to.be.false; + expect(Object.keys(eventCallbacks).length).toBe(3); + expect(dataSpy.mock.calls).toEqual([['a'], ['b']]); + expect(errorSpy.mock.calls[0][0]).toEqual(err); + expect(completeSpy).not.toHaveBeenCalled(); + expect(callMock.cancel).not.toHaveBeenCalled(); }); it('handles client side cancel', () => { @@ -239,12 +230,10 @@ describe('ClientGrpcProxy', () => { eventCallbacks.end(); eventCallbacks.data('c'); - expect(callMock.cancel.called, 'should call call.cancel()').to.be.true; - expect(callMock.destroy.called, 'should call call.destroy()').to.be - .true; - expect(dataSpy.args).to.eql([['a'], ['b']]); - expect(errorSpy.called, 'should not error if client canceled').to.be - .false; + expect(callMock.cancel).toHaveBeenCalled(); + expect(callMock.destroy).toHaveBeenCalled(); + expect(dataSpy.mock.calls).toEqual([['a'], ['b']]); + expect(errorSpy).not.toHaveBeenCalled(); }); }); }); @@ -256,7 +245,7 @@ describe('ClientGrpcProxy', () => { { [methodKey]: {} }, methodKey, ); - expect(fn()).to.be.instanceof(Observable); + expect(fn()).toBeInstanceOf(Observable); }); describe('on subscribe', () => { const methodName = 'm'; @@ -277,13 +266,13 @@ describe('ClientGrpcProxy', () => { }); it('should call native method', () => { - const spy = sinon.spy(obj, methodName); + const spy = vi.spyOn(obj, methodName); stream$.subscribe({ next: () => ({}), error: () => ({}), }); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); describe('when stream request', () => { @@ -291,11 +280,11 @@ describe('ClientGrpcProxy', () => { err: Error | null | undefined, response: any, ) => void; - const writeSpy = sinon.spy(); + const writeSpy = vi.fn(); const methodName = 'm'; const callMock = { - cancel: sinon.spy(), + cancel: vi.fn(), finished: false, write: writeSpy, }; @@ -321,15 +310,15 @@ describe('ClientGrpcProxy', () => { }); it('should subscribe to request upstream', () => { - const upstreamSubscribe = sinon.spy(upstream, 'subscribe'); + const upstreamSubscribe = vi.spyOn(upstream, 'subscribe'); stream$.subscribe({ next: () => ({}), error: () => ({}), }); upstream.next({ test: true }); - expect(writeSpy.called).to.be.true; - expect(upstreamSubscribe.called).to.be.true; + expect(writeSpy).toHaveBeenCalled(); + expect(upstreamSubscribe).toHaveBeenCalled(); }); }); @@ -337,12 +326,12 @@ describe('ClientGrpcProxy', () => { it('should cancel call on client unsubscribe', () => { const methodName = 'm'; - const dataSpy = sinon.spy(); - const errorSpy = sinon.spy(); - const completeSpy = sinon.spy(); + const dataSpy = vi.fn(); + const errorSpy = vi.fn(); + const completeSpy = vi.fn(); const callMock = { - cancel: sinon.spy(), + cancel: vi.fn(), finished: false, }; @@ -367,22 +356,22 @@ describe('ClientGrpcProxy', () => { subscription.unsubscribe(); handler!(null, 'a'); - expect(dataSpy.called).to.be.false; - expect(errorSpy.called).to.be.false; - expect(completeSpy.called).to.be.false; - expect(callMock.cancel.called).to.be.true; + expect(dataSpy).not.toHaveBeenCalled(); + expect(errorSpy).not.toHaveBeenCalled(); + expect(completeSpy).not.toHaveBeenCalled(); + expect(callMock.cancel).toHaveBeenCalled(); }); it('should cancel call on client unsubscribe case client streaming', () => { const methodName = 'm'; - const dataSpy = sinon.spy(); - const errorSpy = sinon.spy(); - const completeSpy = sinon.spy(); - const writeSpy = sinon.spy(); + const dataSpy = vi.fn(); + const errorSpy = vi.fn(); + const completeSpy = vi.fn(); + const writeSpy = vi.fn(); const callMock = { - cancel: sinon.spy(), + cancel: vi.fn(), finished: false, write: writeSpy, }; @@ -402,7 +391,7 @@ describe('ClientGrpcProxy', () => { methodName, )(upstream); - const upstreamSubscribe = sinon.spy(upstream, 'subscribe'); + const upstreamSubscribe = vi.spyOn(upstream, 'subscribe'); stream$.subscribe({ next: () => ({}), error: () => ({}), @@ -418,12 +407,12 @@ describe('ClientGrpcProxy', () => { subscription.unsubscribe(); handler!(null, 'a'); - expect(dataSpy.called).to.be.false; - expect(writeSpy.called).to.be.true; - expect(errorSpy.called).to.be.false; - expect(completeSpy.called).to.be.false; - expect(callMock.cancel.called).to.be.true; - expect(upstreamSubscribe.called).to.be.true; + expect(dataSpy).not.toHaveBeenCalled(); + expect(writeSpy).toHaveBeenCalled(); + expect(errorSpy).not.toHaveBeenCalled(); + expect(completeSpy).not.toHaveBeenCalled(); + expect(callMock.cancel).toHaveBeenCalled(); + expect(upstreamSubscribe).toHaveBeenCalled(); }); }); }); @@ -431,13 +420,13 @@ describe('ClientGrpcProxy', () => { describe('createClients', () => { describe('when package does not exist', () => { it('should throw "InvalidGrpcPackageException"', () => { - sinon.stub(client, 'lookupPackage').callsFake(() => null); + vi.spyOn(client, 'lookupPackage').mockImplementation(() => null); untypedClient.logger = new NoopLogger(); try { client.createClients(); } catch (err) { - expect(err).to.be.instanceof(InvalidGrpcPackageException); + expect(err).toBeInstanceOf(InvalidGrpcPackageException); } }); }); @@ -446,56 +435,51 @@ describe('ClientGrpcProxy', () => { describe('loadProto', () => { describe('when proto is invalid', () => { it('should throw InvalidProtoDefinitionException', () => { - const getPackageDefinitionStub = sinon.stub( - grpcHelpers, - 'getGrpcPackageDefinition' as any, - ); - getPackageDefinitionStub.callsFake(() => { - throw new Error(); - }); - untypedClient.logger = new NoopLogger(); - expect(() => client.loadProto()).to.throws( - InvalidProtoDefinitionException, - ); - getPackageDefinitionStub.restore(); + expect( + () => + new ClientGrpcProxy({ + protoPath: '/nonexistent/invalid.proto', + package: 'test', + }), + ).toThrow(InvalidProtoDefinitionException); }); }); }); describe('close', () => { it('should call "close" method', () => { - const grpcClient = { close: sinon.spy() }; + const grpcClient = { close: vi.fn() }; untypedClient.clients.set('test', grpcClient); untypedClient.grpcClients[0] = {}; client.close(); - expect(grpcClient.close.called).to.be.true; - expect(untypedClient.clients.size).to.be.eq(0); - expect(untypedClient.grpcClients.length).to.be.eq(0); + expect(grpcClient.close).toHaveBeenCalled(); + expect(untypedClient.clients.size).toBe(0); + expect(untypedClient.grpcClients.length).toBe(0); }); }); describe('publish', () => { it('should throw exception', () => { - expect(() => client['publish'](null, null!)).to.throws(Error); + expect(() => client['publish'](null, null!)).toThrow(Error); }); }); describe('send', () => { it('should throw exception', () => { - expect(() => client.send(null, null)).to.throws(Error); + expect(() => client.send(null, null)).toThrow(Error); }); }); describe('connect', () => { it('should throw exception', () => { - client.connect().catch(error => expect(error).to.be.instanceof(Error)); + client.connect().catch(error => expect(error).toBeInstanceOf(Error)); }); }); describe('dispatchEvent', () => { it('should throw exception', () => { client['dispatchEvent'](null).catch(error => - expect(error).to.be.instanceof(Error), + expect(error).toBeInstanceOf(Error), ); }); }); @@ -504,8 +488,8 @@ describe('ClientGrpcProxy', () => { it('should return root package in case package name is not defined', () => { const root = {}; - expect(client.lookupPackage(root, undefined!)).to.be.equal(root); - expect(client.lookupPackage(root, '')).to.be.equal(root); + expect(client.lookupPackage(root, undefined!)).toBe(root); + expect(client.lookupPackage(root, '')).toBe(root); }); }); }); diff --git a/packages/microservices/test/client/client-kafka.spec.ts b/packages/microservices/test/client/client-kafka.spec.ts index 773a9859027..e7ffb717687 100644 --- a/packages/microservices/test/client/client-kafka.spec.ts +++ b/packages/microservices/test/client/client-kafka.spec.ts @@ -1,16 +1,14 @@ -import { expect } from 'chai'; import { Producer } from 'kafkajs'; import { Observable } from 'rxjs'; -import * as sinon from 'sinon'; -import { ClientKafka } from '../../client/client-kafka'; -import { NO_MESSAGE_HANDLER } from '../../constants'; -import { KafkaHeaders } from '../../enums'; -import { InvalidKafkaClientTopicException } from '../../errors/invalid-kafka-client-topic.exception'; +import { ClientKafka } from '../../client/client-kafka.js'; +import { NO_MESSAGE_HANDLER } from '../../constants.js'; +import { KafkaHeaders } from '../../enums/index.js'; +import { InvalidKafkaClientTopicException } from '../../errors/invalid-kafka-client-topic.exception.js'; import { ConsumerGroupJoinEvent, EachMessagePayload, KafkaMessage, -} from '../../external/kafka.interface'; +} from '../../external/kafka.interface.js'; describe('ClientKafka', () => { const topic = 'test.topic'; @@ -152,29 +150,29 @@ describe('ClientKafka', () => { let client: ClientKafka; let untypedClient: any; - let callback: sinon.SinonSpy; - let connect: sinon.SinonSpy; - let subscribe: sinon.SinonSpy; - let run: sinon.SinonSpy; - let send: sinon.SinonSpy; - let on: sinon.SinonSpy; - let consumerStub: sinon.SinonStub; - let producerStub: sinon.SinonStub; - let createClientStub: sinon.SinonStub; + let callback: ReturnType; + let connect: ReturnType; + let subscribe: ReturnType; + let run: ReturnType; + let send: ReturnType; + let on: ReturnType; + let consumerStub: ReturnType; + let producerStub: ReturnType; + let createClientStub: ReturnType; let kafkaClient: any; beforeEach(() => { client = new ClientKafka({}); untypedClient = client as any; - callback = sinon.spy(); - connect = sinon.spy(); - subscribe = sinon.spy(); - run = sinon.spy(); - send = sinon.spy(); - on = sinon.spy(); + callback = vi.fn(); + connect = vi.fn(); + subscribe = vi.fn(); + run = vi.fn(); + send = vi.fn(); + on = vi.fn(); - consumerStub = sinon.stub(client as any, 'consumer').callsFake(() => { + consumerStub = vi.fn().mockImplementation(() => { return { connect, subscribe, @@ -200,7 +198,7 @@ describe('ClientKafka', () => { on, }; }); - producerStub = sinon.stub(client as any, 'producer').callsFake(() => { + producerStub = vi.fn().mockImplementation(() => { return { connect, send, @@ -219,9 +217,9 @@ describe('ClientKafka', () => { producer: producerStub, }; - createClientStub = sinon - .stub(client, 'createClient') - .callsFake(() => kafkaClient); + createClientStub = vi + .spyOn(client, 'createClient') + .mockImplementation(() => kafkaClient); }); describe('createClient', () => { @@ -229,8 +227,8 @@ describe('ClientKafka', () => { client = new ClientKafka({}); }); - it(`should accept a custom logCreator in client options`, () => { - const logCreatorSpy = sinon.spy(() => 'test'); + it(`should accept a custom logCreator in client options`, async () => { + const logCreatorSpy = vi.fn().mockReturnValue('test'); const logCreator = () => logCreatorSpy; client = new ClientKafka({ @@ -240,21 +238,22 @@ describe('ClientKafka', () => { }, }); - const logger = client.createClient().logger(); + const kafkaClient = await client.createClient(); + const logger = kafkaClient.logger(); logger.info({ namespace: '', level: 1, log: 'test' }); - expect(logCreatorSpy.called).to.be.true; + expect(logCreatorSpy).toHaveBeenCalled(); }); }); describe('subscribeToResponseOf', () => { - let normalizePatternSpy: sinon.SinonSpy; - let getResponsePatternNameSpy: sinon.SinonSpy; + let normalizePatternSpy: ReturnType; + let getResponsePatternNameSpy: ReturnType; beforeEach(() => { - normalizePatternSpy = sinon.spy(client as any, 'normalizePattern'); - getResponsePatternNameSpy = sinon.spy( + normalizePatternSpy = vi.spyOn(client as any, 'normalizePattern'); + getResponsePatternNameSpy = vi.spyOn( client as any, 'getResponsePatternName', ); @@ -263,20 +262,20 @@ describe('ClientKafka', () => { it(`should create an array of response patterns`, () => { client.subscribeToResponseOf(topic); - expect(normalizePatternSpy.calledWith(topic)).to.be.true; - expect(getResponsePatternNameSpy.calledWith(topic)).to.be.true; - expect(client['responsePatterns']).to.not.be.empty; - expect(client['responsePatterns'][0]).to.eq(replyTopic); + expect(normalizePatternSpy).toHaveBeenCalledWith(topic); + expect(getResponsePatternNameSpy).toHaveBeenCalledWith(topic); + expect(client['responsePatterns']).not.toHaveLength(0); + expect(client['responsePatterns'][0]).toEqual(replyTopic); }); afterEach(() => { - normalizePatternSpy.restore(); + normalizePatternSpy.mockRestore(); }); }); describe('close', () => { - const consumer = { disconnect: sinon.stub().resolves() }; - const producer = { disconnect: sinon.stub().resolves() }; + const consumer = { disconnect: vi.fn().mockResolvedValue(undefined) }; + const producer = { disconnect: vi.fn().mockResolvedValue(undefined) }; beforeEach(() => { untypedClient._consumer = consumer; untypedClient._producer = producer; @@ -284,40 +283,37 @@ describe('ClientKafka', () => { it('should close server', async () => { await client.close(); - expect(consumer.disconnect.calledOnce).to.be.true; - expect(producer.disconnect.calledOnce).to.be.true; - expect(untypedClient._consumer).to.be.null; - expect(untypedClient._producer).to.be.null; - expect(untypedClient.client).to.be.null; + expect(consumer.disconnect).toHaveBeenCalledOnce(); + expect(producer.disconnect).toHaveBeenCalledOnce(); + expect(untypedClient._consumer).toBeNull(); + expect(untypedClient._producer).toBeNull(); + expect(untypedClient.client).toBeNull(); }); }); describe('connect', () => { - let consumerAssignmentsStub: sinon.SinonStub; - let bindTopicsStub: sinon.SinonStub; + let consumerAssignmentsStub: any; + let bindTopicsStub: ReturnType; describe('consumer and producer', () => { beforeEach(() => { - consumerAssignmentsStub = sinon.stub( - client as any, - 'consumerAssignments', - ); - bindTopicsStub = sinon - .stub(client, 'bindTopics') - .callsFake(async () => {}); + consumerAssignmentsStub = (client as any).consumerAssignments; + bindTopicsStub = vi + .spyOn(client, 'bindTopics') + .mockImplementation(async () => {}); }); it('should expect the connection to be created', async () => { const connection = await client.connect(); - expect(createClientStub.calledOnce).to.be.true; - expect(producerStub.calledOnce).to.be.true; - expect(consumerStub.calledOnce).to.be.true; - expect(on.called).to.be.true; - expect(client['consumerAssignments']).to.be.empty; - expect(connect.calledTwice).to.be.true; - expect(bindTopicsStub.calledOnce).to.be.true; - expect(connection).to.deep.equal(producerStub()); + expect(createClientStub).toHaveBeenCalledOnce(); + expect(producerStub).toHaveBeenCalledOnce(); + expect(consumerStub).toHaveBeenCalledOnce(); + expect(on).toHaveBeenCalled(); + expect(client['consumerAssignments']).toEqual({}); + expect(connect).toHaveBeenCalledTimes(2); + expect(bindTopicsStub).toHaveBeenCalledOnce(); + expect(connection).toEqual(producerStub()); }); it('should expect the connection to be reused', async () => { @@ -325,46 +321,42 @@ describe('ClientKafka', () => { await client.connect(); - expect(createClientStub.calledOnce).to.be.false; - expect(producerStub.calledOnce).to.be.false; - expect(consumerStub.calledOnce).to.be.false; + expect(createClientStub).not.toHaveBeenCalled(); + expect(producerStub).not.toHaveBeenCalled(); + expect(consumerStub).not.toHaveBeenCalled(); - expect(on.calledOnce).to.be.false; - expect(client['consumerAssignments']).to.be.empty; + expect(on).not.toHaveBeenCalled(); + expect(client['consumerAssignments']).toEqual({}); - expect(connect.calledTwice).to.be.false; + expect(connect).not.toHaveBeenCalledTimes(2); - expect(bindTopicsStub.calledOnce).to.be.false; + expect(bindTopicsStub).not.toHaveBeenCalled(); }); }); describe('producer only mode', () => { beforeEach(() => { - consumerAssignmentsStub = sinon.stub( - client as any, - 'consumerAssignments', - ); - bindTopicsStub = sinon - .stub(client, 'bindTopics') - .callsFake(async () => {}); + consumerAssignmentsStub = (client as any).consumerAssignments; + bindTopicsStub = vi + .spyOn(client, 'bindTopics') + .mockImplementation(async () => {}); client['producerOnlyMode'] = true; }); it('should expect the connection to be created', async () => { const connection = await client.connect(); - expect(createClientStub.calledOnce).to.be.true; - expect(producerStub.calledOnce).to.be.true; + expect(createClientStub).toHaveBeenCalledOnce(); + expect(producerStub).toHaveBeenCalledOnce(); - expect(consumerStub.calledOnce).to.be.false; + expect(consumerStub).not.toHaveBeenCalled(); - expect(on.calledOnce).to.be.false; - expect(client['consumerAssignments']).to.be.empty; + expect(client['consumerAssignments']).toEqual({}); - expect(connect.calledOnce).to.be.true; + expect(connect).toHaveBeenCalledOnce(); - expect(bindTopicsStub.calledOnce).to.be.false; - expect(connection).to.deep.equal(producerStub()); + expect(bindTopicsStub).not.toHaveBeenCalled(); + expect(connection).toEqual(producerStub()); }); it('should expect the connection to be reused', async () => { @@ -372,16 +364,16 @@ describe('ClientKafka', () => { await client.connect(); - expect(createClientStub.calledOnce).to.be.false; - expect(producerStub.calledOnce).to.be.false; - expect(consumerStub.calledOnce).to.be.false; + expect(createClientStub).not.toHaveBeenCalled(); + expect(producerStub).not.toHaveBeenCalled(); + expect(consumerStub).not.toHaveBeenCalled(); - expect(on.calledOnce).to.be.false; - expect(client['consumerAssignments']).to.be.empty; + expect(on).not.toHaveBeenCalled(); + expect(client['consumerAssignments']).toEqual({}); - expect(connect.calledTwice).to.be.false; + expect(connect).not.toHaveBeenCalledTimes(2); - expect(bindTopicsStub.calledOnce).to.be.false; + expect(bindTopicsStub).not.toHaveBeenCalled(); }); }); }); @@ -410,7 +402,7 @@ describe('ClientKafka', () => { client['setConsumerAssignments'](consumerAssignments); - expect(client['consumerAssignments']).to.deep.eq( + expect(client['consumerAssignments']).toEqual( // consumerAssignments.payload.memberAssignment, { 'topic-a': 0, @@ -442,7 +434,7 @@ describe('ClientKafka', () => { client['setConsumerAssignments'](consumerAssignments); - expect(client['consumerAssignments']).to.deep.eq({ + expect(client['consumerAssignments']).toEqual({ 'topic-b': 3, }); }); @@ -455,13 +447,11 @@ describe('ClientKafka', () => { await client.bindTopics(); - expect(subscribe.calledOnce).to.be.true; - expect( - subscribe.calledWith({ - topics: [replyTopic], - }), - ).to.be.true; - expect(run.calledOnce).to.be.true; + expect(subscribe).toHaveBeenCalledOnce(); + expect(subscribe).toHaveBeenCalledWith({ + topics: [replyTopic], + }); + expect(run).toHaveBeenCalledOnce(); }); it('should bind topics from response patterns with options', async () => { @@ -472,14 +462,12 @@ describe('ClientKafka', () => { await client.bindTopics(); - expect(subscribe.calledOnce).to.be.true; - expect( - subscribe.calledWith({ - topics: [replyTopic], - fromBeginning: true, - }), - ).to.be.true; - expect(run.calledOnce).to.be.true; + expect(subscribe).toHaveBeenCalledOnce(); + expect(subscribe).toHaveBeenCalledWith({ + topics: [replyTopic], + fromBeginning: true, + }); + expect(run).toHaveBeenCalledOnce(); }); }); @@ -494,13 +482,11 @@ describe('ClientKafka', () => { subscription(payload); }); it('should call callback with expected arguments', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - err: undefined, - response: messageValue, - }), - ).to.be.true; + expect(callback).toHaveBeenCalled(); + expect(callback).toHaveBeenCalledWith({ + err: undefined, + response: messageValue, + }); }); }); @@ -513,14 +499,12 @@ describe('ClientKafka', () => { }); it('should call callback with dispose param', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - isDisposed: true, - response: deserializedPayloadDisposed.message.value, - err: undefined, - }), - ).to.be.true; + expect(callback).toHaveBeenCalled(); + expect(callback).toHaveBeenCalledWith({ + isDisposed: true, + response: deserializedPayloadDisposed.message.value, + err: undefined, + }); }); }); @@ -533,14 +517,12 @@ describe('ClientKafka', () => { }); it('should call callback with error param', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - isDisposed: true, - response: undefined, - err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + expect(callback).toHaveBeenCalled(); + expect(callback).toHaveBeenCalledWith({ + isDisposed: true, + response: undefined, + err: NO_MESSAGE_HANDLER, + }); }); }); @@ -553,13 +535,13 @@ describe('ClientKafka', () => { }); it('should not call callback', () => { - expect(callback.called).to.be.false; + expect(callback).not.toHaveBeenCalled(); }); }); describe('disposed and "id" is incorrect', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createResponseCallback(); client['routingMap'].set('incorrect-correlation-id', callback); @@ -567,7 +549,7 @@ describe('ClientKafka', () => { }); it('should not call callback', () => { - expect(callback.called).to.be.false; + expect(callback).not.toHaveBeenCalled(); }); }); }); @@ -580,23 +562,23 @@ describe('ClientKafka', () => { messages: [], }, ); - expect(stream$ instanceof Observable).to.be.true; + expect(stream$ instanceof Observable).toBe(true); }); it(`should call "connect" immediately`, () => { - const connectSpy = sinon.spy(client, 'connect'); + const connectSpy = vi.spyOn(client, 'connect'); client.emitBatch( {}, { messages: [], }, ); - expect(connectSpy.calledOnce).to.be.true; + expect(connectSpy).toHaveBeenCalledOnce(); }); describe('when "connect" throws', () => { it('should return Observable with error', () => { - sinon.stub(client, 'connect').callsFake(() => { + vi.spyOn(client, 'connect').mockImplementation(() => { throw new Error(); }); @@ -610,7 +592,7 @@ describe('ClientKafka', () => { stream$.subscribe({ next: () => {}, error: err => { - expect(err).to.be.instanceof(Error); + expect(err).toBeInstanceOf(Error); }, }); }); @@ -618,28 +600,28 @@ describe('ClientKafka', () => { describe('when is connected', () => { beforeEach(() => { - sinon - .stub(client, 'connect') - .callsFake(() => Promise.resolve({} as Producer)); + vi.spyOn(client, 'connect').mockImplementation(() => + Promise.resolve({} as Producer), + ); }); it(`should call dispatchBatchEvent`, () => { const pattern = { test: 3 }; const data = { messages: [] }; - const dispatchBatchEventSpy = sinon - .stub() - .callsFake(() => Promise.resolve(true)); + const dispatchBatchEventSpy = vi + .fn() + .mockImplementation(() => Promise.resolve(true)); const stream$ = client.emitBatch(pattern, data); client['dispatchBatchEvent'] = dispatchBatchEventSpy; stream$.subscribe(() => { - expect(dispatchBatchEventSpy.calledOnce).to.be.true; + expect(dispatchBatchEventSpy).toHaveBeenCalledOnce(); }); }); }); it('should return Observable with error', () => { const err$ = client.emitBatch(null, null!); - expect(err$).to.be.instanceOf(Observable); + expect(err$).toBeInstanceOf(Observable); }); }); @@ -650,39 +632,39 @@ describe('ClientKafka', () => { data: messageValue, }; - let sendStub: sinon.SinonStub; - let sendSpy: sinon.SinonSpy; + let sendStub: ReturnType; + let sendSpy: ReturnType; beforeEach(() => { - sendStub = sinon.stub().callsFake(async a => { + sendStub = vi.fn().mockImplementation(async a => { throw new Error('ERROR!'); }); - sendSpy = sinon.spy(); + sendSpy = vi.fn(); }); it('should publish packet', async () => { - sinon.stub(client as any, '_producer').value({ + vi.spyOn(client as any, '_producer', 'get').mockReturnValue({ send: sendSpy, }); await client['dispatchEvent'](eventMessage); - expect(sendSpy.calledOnce).to.be.true; - expect(sendSpy.args[0][0].topic).to.eq(topic); - expect(sendSpy.args[0][0].messages).to.not.be.empty; + expect(sendSpy).toHaveBeenCalledOnce(); + expect(sendSpy.mock.calls[0][0].topic).toEqual(topic); + expect(sendSpy.mock.calls[0][0].messages).not.toHaveLength(0); - const sentMessage = sendSpy.args[0][0].messages[0]; + const sentMessage = sendSpy.mock.calls[0][0].messages[0]; - expect(sentMessage.value).to.eq(messageValue); + expect(sentMessage.value).toEqual(messageValue); }); it('should throw error', async () => { - sinon.stub(client as any, 'producer').value({ + vi.spyOn(client as any, 'producer', 'get').mockReturnValue({ send: sendStub, }); client['dispatchEvent'](eventMessage).catch(err => - expect(err).to.be.instanceOf(Error), + expect(err).toBeInstanceOf(Error), ); }); }); @@ -695,7 +677,7 @@ describe('ClientKafka', () => { const result = client.getConsumerAssignments(); - expect(result).to.deep.eq(client['consumerAssignments']); + expect(result).toEqual(client['consumerAssignments']); }); }); @@ -707,13 +689,13 @@ describe('ClientKafka', () => { const result = client['getReplyTopicPartition'](replyTopic); - expect(result).to.eq('0'); + expect(result).toEqual('0'); }); it('should throw error when the topic is not being consumed', () => { client['consumerAssignments'] = {}; - expect(() => client['getReplyTopicPartition'](replyTopic)).to.throw( + expect(() => client['getReplyTopicPartition'](replyTopic)).toThrow( InvalidKafkaClientTopicException, ); }); @@ -723,7 +705,7 @@ describe('ClientKafka', () => { [topic]: undefined!, }; - expect(() => client['getReplyTopicPartition'](replyTopic)).to.throw( + expect(() => client['getReplyTopicPartition'](replyTopic)).toThrow( InvalidKafkaClientTopicException, ); }); @@ -737,35 +719,35 @@ describe('ClientKafka', () => { data: messageValue, }; - let assignPacketIdStub: sinon.SinonStub; - let normalizePatternSpy: sinon.SinonSpy; - let getResponsePatternNameSpy: sinon.SinonSpy; - let getReplyTopicPartitionSpy: sinon.SinonSpy; - let routingMapSetSpy: sinon.SinonSpy; - let sendSpy: sinon.SinonSpy; + let assignPacketIdStub: any; + let normalizePatternSpy: any; + let getResponsePatternNameSpy: any; + let getReplyTopicPartitionSpy: any; + let routingMapSetSpy: any; + let sendSpy: ReturnType; beforeEach(() => { - normalizePatternSpy = sinon.spy(client as any, 'normalizePattern'); - getResponsePatternNameSpy = sinon.spy( + normalizePatternSpy = vi.spyOn(client as any, 'normalizePattern'); + getResponsePatternNameSpy = vi.spyOn( client as any, 'getResponsePatternName', ); - getReplyTopicPartitionSpy = sinon.spy( + getReplyTopicPartitionSpy = vi.spyOn( client as any, 'getReplyTopicPartition', ); - routingMapSetSpy = sinon.spy(untypedClient.routingMap, 'set'); - sendSpy = sinon.spy(() => Promise.resolve()); + routingMapSetSpy = vi.spyOn(untypedClient.routingMap, 'set'); + sendSpy = vi.fn().mockResolvedValue(undefined); - assignPacketIdStub = sinon - .stub(client as any, 'assignPacketId') - .callsFake(packet => + assignPacketIdStub = vi + .spyOn(client as any, 'assignPacketId') + .mockImplementation(packet => Object.assign(packet as object, { id: correlationId, }), ); - sinon.stub(client as any, '_producer').value({ + vi.spyOn(client as any, '_producer', 'get').mockReturnValue({ send: sendSpy, }); @@ -779,7 +761,7 @@ describe('ClientKafka', () => { await waitForNextTick(); - expect(assignPacketIdStub.calledWith(readPacket)).to.be.true; + expect(assignPacketIdStub).toHaveBeenCalledWith(readPacket); }); it('should normalize the pattern', async () => { @@ -787,7 +769,7 @@ describe('ClientKafka', () => { await waitForNextTick(); - expect(normalizePatternSpy.calledWith(topic)).to.be.true; + expect(normalizePatternSpy).toHaveBeenCalledWith(topic); }); it('should get the reply pattern', async () => { @@ -795,7 +777,7 @@ describe('ClientKafka', () => { await waitForNextTick(); - expect(getResponsePatternNameSpy.calledWith(topic)).to.be.true; + expect(getResponsePatternNameSpy).toHaveBeenCalledWith(topic); }); it('should get the reply partition', async () => { @@ -803,7 +785,7 @@ describe('ClientKafka', () => { await waitForNextTick(); - expect(getReplyTopicPartitionSpy.calledWith(replyTopic)).to.be.true; + expect(getReplyTopicPartitionSpy).toHaveBeenCalledWith(replyTopic); }); it('should add the callback to the routing map', async () => { @@ -811,9 +793,9 @@ describe('ClientKafka', () => { await waitForNextTick(); - expect(routingMapSetSpy.calledOnce).to.be.true; - expect(routingMapSetSpy.args[0][0]).to.eq(correlationId); - expect(routingMapSetSpy.args[0][1]).to.eq(callback); + expect(routingMapSetSpy).toHaveBeenCalledOnce(); + expect(routingMapSetSpy.mock.calls[0][0]).toEqual(correlationId); + expect(routingMapSetSpy.mock.calls[0][1]).toEqual(callback); }); it('should send the message with headers', async () => { @@ -821,19 +803,19 @@ describe('ClientKafka', () => { await waitForNextTick(); - expect(sendSpy.calledOnce).to.be.true; - expect(sendSpy.args[0][0].topic).to.eq(topic); - expect(sendSpy.args[0][0].messages).to.not.be.empty; + expect(sendSpy).toHaveBeenCalledOnce(); + expect(sendSpy.mock.calls[0][0].topic).toEqual(topic); + expect(sendSpy.mock.calls[0][0].messages).not.toHaveLength(0); - const sentMessage = sendSpy.args[0][0].messages[0]; + const sentMessage = sendSpy.mock.calls[0][0].messages[0]; - expect(sentMessage.value).to.eq(messageValue); - expect(sentMessage.headers).to.not.be.empty; - expect(sentMessage.headers[KafkaHeaders.CORRELATION_ID]).to.eq( + expect(sentMessage.value).toEqual(messageValue); + expect(sentMessage.headers).toBeDefined(); + expect(sentMessage.headers[KafkaHeaders.CORRELATION_ID]).toEqual( correlationId, ); - expect(sentMessage.headers[KafkaHeaders.REPLY_TOPIC]).to.eq(replyTopic); - expect(sentMessage.headers[KafkaHeaders.REPLY_PARTITION]).to.eq( + expect(sentMessage.headers[KafkaHeaders.REPLY_TOPIC]).toEqual(replyTopic); + expect(sentMessage.headers[KafkaHeaders.REPLY_PARTITION]).toEqual( replyPartition, ); }); @@ -843,26 +825,28 @@ describe('ClientKafka', () => { await waitForNextTick(); - expect(client['routingMap'].has(correlationId)).to.be.false; - expect(client['routingMap'].size).to.eq(0); + expect(client['routingMap'].has(correlationId)).toBe(false); + expect(client['routingMap'].size).toEqual(0); }); describe('on error', () => { - let clientProducerStub: sinon.SinonStub; - let sendStub: sinon.SinonStub; + let clientProducerStub: any; + let sendStub: ReturnType; beforeEach(() => { - sendStub = sinon.stub().callsFake(() => { + sendStub = vi.fn().mockImplementation(() => { throw new Error(); }); - clientProducerStub = sinon.stub(client as any, '_producer').value({ - send: sendStub, - }); + clientProducerStub = vi + .spyOn(client as any, '_producer', 'get') + .mockReturnValue({ + send: sendStub, + }); }); afterEach(() => { - clientProducerStub.restore(); + clientProducerStub.mockRestore(); }); it('should call callback', async () => { @@ -870,41 +854,41 @@ describe('ClientKafka', () => { return new Promise(async resolve => { return client['publish'](readPacket, ({ err }) => resolve(err)); }).then(err => { - expect(err).to.be.instanceof(Error); + expect(err).toBeInstanceOf(Error); }); }); }); describe('dispose callback', () => { let subscription; - let getResponsePatternNameStub: sinon.SinonStub; - let getReplyTopicPartitionStub: sinon.SinonStub; + let getResponsePatternNameStub: any; + let getReplyTopicPartitionStub: any; beforeEach(async () => { // restore - getResponsePatternNameSpy.restore(); - getReplyTopicPartitionSpy.restore(); + getResponsePatternNameSpy.mockRestore(); + getReplyTopicPartitionSpy.mockRestore(); // return the topic instead of the reply topic - getResponsePatternNameStub = sinon - .stub(client as any, 'getResponsePatternName') - .callsFake(() => topic); - getReplyTopicPartitionStub = sinon - .stub(client as any, 'getReplyTopicPartition') - .callsFake(() => '0'); + getResponsePatternNameStub = vi + .spyOn(client as any, 'getResponsePatternName') + .mockImplementation(() => topic); + getReplyTopicPartitionStub = vi + .spyOn(client as any, 'getReplyTopicPartition') + .mockImplementation(() => '0'); subscription = client['publish'](readPacket, callback); subscription(payloadDisposed); }); afterEach(() => { - getResponsePatternNameStub.restore(); - getReplyTopicPartitionStub.restore(); + getResponsePatternNameStub.mockRestore(); + getReplyTopicPartitionStub.mockRestore(); }); it('should remove callback from routing map', async () => { - expect(client['routingMap'].has(correlationId)).to.be.false; - expect(client['routingMap'].size).to.eq(0); + expect(client['routingMap'].has(correlationId)).toBe(false); + expect(client['routingMap'].size).toEqual(0); }); }); }); diff --git a/packages/microservices/test/client/client-mqtt.spec.ts b/packages/microservices/test/client/client-mqtt.spec.ts index 35845b66855..87f461c5b1b 100644 --- a/packages/microservices/test/client/client-mqtt.spec.ts +++ b/packages/microservices/test/client/client-mqtt.spec.ts @@ -1,10 +1,7 @@ -import { expect } from 'chai'; import { EMPTY } from 'rxjs'; -import * as sinon from 'sinon'; -import { ClientMqtt } from '../../client/client-mqtt'; -import { MqttEventsMap } from '../../events/mqtt.events'; -import { ReadPacket } from '../../interfaces'; -import { MqttRecord } from '../../record-builders'; +import { ClientMqtt } from '../../client/client-mqtt.js'; +import { ReadPacket } from '../../interfaces/index.js'; +import { MqttRecord } from '../../record-builders/index.js'; describe('ClientMqtt', () => { const test = 'test'; @@ -13,25 +10,25 @@ describe('ClientMqtt', () => { describe('getRequestPattern', () => { it(`should leave pattern as it is`, () => { - expect(client.getRequestPattern(test)).to.equal(test); + expect(client.getRequestPattern(test)).toBe(test); }); }); describe('getResponsePattern', () => { it(`should append "/reply" to string`, () => { const expectedResult = test + '/reply'; - expect(client.getResponsePattern(test)).to.equal(expectedResult); + expect(client.getResponsePattern(test)).toBe(expectedResult); }); }); describe('publish', () => { const pattern = 'test'; let msg: ReadPacket; - let subscribeSpy: sinon.SinonSpy, - publishSpy: sinon.SinonSpy, - onSpy: sinon.SinonSpy, - removeListenerSpy: sinon.SinonSpy, - unsubscribeSpy: sinon.SinonSpy, - connectSpy: sinon.SinonStub, - assignStub: sinon.SinonStub, + let subscribeSpy: ReturnType, + publishSpy: ReturnType, + onSpy: ReturnType, + removeListenerSpy: ReturnType, + unsubscribeSpy: ReturnType, + connectSpy: ReturnType, + assignStub: ReturnType, mqttClient: any; const id = '1'; @@ -40,11 +37,11 @@ describe('ClientMqtt', () => { untypedClient = client as any; msg = { pattern, data: 'data' }; - subscribeSpy = sinon.spy((name, fn) => fn()); - publishSpy = sinon.spy(); - onSpy = sinon.spy(); - removeListenerSpy = sinon.spy(); - unsubscribeSpy = sinon.spy(); + subscribeSpy = vi.fn((name, fn) => fn()); + publishSpy = vi.fn(); + onSpy = vi.fn(); + removeListenerSpy = vi.fn(); + unsubscribeSpy = vi.fn(); mqttClient = { subscribe: subscribeSpy, @@ -55,72 +52,75 @@ describe('ClientMqtt', () => { addListener: () => ({}), }; untypedClient.mqttClient = mqttClient; - connectSpy = sinon.stub(client, 'connect'); - assignStub = sinon - .stub(client, 'assignPacketId' as any) - .callsFake(packet => Object.assign(packet as object, { id })); + connectSpy = vi + .spyOn(client, 'connect') + .mockImplementation(() => ({}) as any); + assignStub = vi + .spyOn(client, 'assignPacketId' as any) + .mockImplementation(packet => Object.assign(packet as object, { id })); }); afterEach(() => { - connectSpy.restore(); - assignStub.restore(); + connectSpy.mockRestore(); + assignStub.mockRestore(); }); it('should subscribe to response pattern name', async () => { client['publish'](msg, () => {}); - expect(subscribeSpy.calledWith(`${pattern}/reply`)).to.be.true; + expect(subscribeSpy.mock.calls[0][0]).toEqual(`${pattern}/reply`); }); it('should publish stringified message to request pattern name', async () => { client['publish'](msg, () => {}); - expect(publishSpy.calledWith(pattern, JSON.stringify(msg))).to.be.true; + expect(publishSpy.mock.calls[0][0]).toEqual(pattern); + expect(publishSpy.mock.calls[0][1]).toEqual(JSON.stringify(msg)); }); it('should add callback to routing map', async () => { client['publish'](msg, () => {}); - expect(client['routingMap'].has(id)).to.be.true; + expect(client['routingMap'].has(id)).toBe(true); }); describe('on error', () => { beforeEach(() => { - assignStub.callsFake(() => { + assignStub.mockImplementation(() => { throw new Error(); }); }); it('should call callback', () => { - const callback = sinon.spy(); + const callback = vi.fn(); client['publish'](msg, callback); - expect(callback.called).to.be.true; - expect(callback.getCall(0).args[0].err).to.be.instanceof(Error); + expect(callback).toHaveBeenCalled(); + expect(callback.mock.calls[0][0].err).toBeInstanceOf(Error); }); }); describe('dispose callback', () => { - let getResponsePatternStub: sinon.SinonStub; - let callback: sinon.SinonSpy, subscription; + let getResponsePatternStub: ReturnType; + let callback: ReturnType, subscription; const channel = 'channel'; beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); - getResponsePatternStub = sinon - .stub(client, 'getResponsePattern') - .callsFake(() => channel); + getResponsePatternStub = vi + .spyOn(client, 'getResponsePattern') + .mockImplementation(() => channel); subscription = client['publish'](msg, callback); subscription(channel, JSON.stringify({ isDisposed: true, id })); }); afterEach(() => { - getResponsePatternStub.restore(); + getResponsePatternStub.mockRestore(); }); it('should unsubscribe to response pattern name', () => { - expect(unsubscribeSpy.calledWith(channel)).to.be.true; + expect(unsubscribeSpy).toHaveBeenCalledWith(channel); }); it('should remove callback from routing map', () => { - expect(client['routingMap'].has(id)).to.be.false; + expect(client['routingMap'].has(id)).toBe(false); }); }); describe('headers', () => { it('should not generate headers if none are configured', async () => { client['publish'](msg, () => {}); - expect(publishSpy.getCall(0).args[2]).to.be.undefined; + expect(publishSpy.mock.calls[0][2]).toBeUndefined(); }); it('should send packet headers', async () => { const requestHeaders = { '1': '123' }; @@ -129,7 +129,7 @@ describe('ClientMqtt', () => { }); client['publish'](msg, () => {}); - expect(publishSpy.getCall(0).args[2].properties.userProperties).to.eql( + expect(publishSpy.mock.calls[0][2].properties.userProperties).toEqual( requestHeaders, ); }); @@ -143,7 +143,7 @@ describe('ClientMqtt', () => { }); client['publish'](msg, () => {}); - expect(publishSpy.getCall(0).args[2].properties.userProperties).to.eql({ + expect(publishSpy.mock.calls[0][2].properties.userProperties).toEqual({ ...staticHeaders, ...requestHeaders, }); @@ -158,14 +158,14 @@ describe('ClientMqtt', () => { }); client['publish'](msg, () => {}); - expect(publishSpy.getCall(0).args[2].properties.userProperties).to.eql( + expect(publishSpy.mock.calls[0][2].properties.userProperties).toEqual( requestHeaders, ); }); }); }); describe('createResponseCallback', () => { - let callback: sinon.SinonSpy, subscription; + let callback: ReturnType, subscription; const responseMessage = { response: 'test', id: '1', @@ -173,24 +173,22 @@ describe('ClientMqtt', () => { describe('not completed', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createResponseCallback(); client['routingMap'].set(responseMessage.id, callback); subscription('channel', Buffer.from(JSON.stringify(responseMessage))); }); it('should call callback with expected arguments', () => { - expect( - callback.calledWith({ - err: undefined, - response: responseMessage.response, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: undefined, + response: responseMessage.response, + }); }); }); describe('disposed and "id" is correct', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createResponseCallback(); client['routingMap'].set(responseMessage.id, callback); @@ -206,19 +204,16 @@ describe('ClientMqtt', () => { }); it('should call callback with dispose param', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - isDisposed: true, - response: responseMessage.response, - err: undefined, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + isDisposed: true, + response: responseMessage.response, + err: undefined, + }); }); }); describe('disposed and "id" is incorrect', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createResponseCallback(); client['routingMap'].set('3', callback); @@ -226,34 +221,34 @@ describe('ClientMqtt', () => { }); it('should not call callback', () => { - expect(callback.called).to.be.false; + expect(callback).not.toHaveBeenCalled(); }); }); }); describe('close', () => { - let endSpy: sinon.SinonSpy; + let endSpy: ReturnType; beforeEach(() => { - endSpy = sinon.spy(); + endSpy = vi.fn(); untypedClient.mqttClient = { endAsync: endSpy }; }); it('should close "pub" when it is not null', async () => { await client.close(); - expect(endSpy.called).to.be.true; + expect(endSpy).toHaveBeenCalled(); }); it('should not close "pub" when it is null', async () => { untypedClient.mqttClient = null; await client.close(); - expect(endSpy.called).to.be.false; + expect(endSpy).not.toHaveBeenCalled(); }); }); describe('connect', () => { - let createClientStub: sinon.SinonStub; - let registerErrorListenerSpy: sinon.SinonSpy; - let connect$Stub: sinon.SinonStub; - let mergeCloseEvent: sinon.SinonStub; + let createClientStub: ReturnType; + let registerErrorListenerSpy: ReturnType; + let connect$Stub: ReturnType; + let mergeCloseEvent: ReturnType; beforeEach(async () => { - createClientStub = sinon.stub(client, 'createClient').callsFake( + createClientStub = vi.spyOn(client, 'createClient').mockImplementation( () => ({ addListener: () => ({}), @@ -261,22 +256,24 @@ describe('ClientMqtt', () => { on: () => ({}), }) as any, ); - registerErrorListenerSpy = sinon.spy(client, 'registerErrorListener'); - connect$Stub = sinon.stub(client, 'connect$' as any).callsFake(() => ({ - subscribe: ({ complete }) => complete(), - pipe() { - return this; - }, - })); - mergeCloseEvent = sinon - .stub(client, 'mergeCloseEvent') - .callsFake((_, source) => source); + registerErrorListenerSpy = vi.spyOn(client, 'registerErrorListener'); + connect$Stub = vi + .spyOn(client, 'connect$' as any) + .mockImplementation(() => ({ + subscribe: ({ complete }) => complete(), + pipe() { + return this; + }, + })); + mergeCloseEvent = vi + .spyOn(client, 'mergeCloseEvent') + .mockImplementation((_, source) => source); }); afterEach(() => { - createClientStub.restore(); - registerErrorListenerSpy.restore(); - connect$Stub.restore(); - mergeCloseEvent.restore(); + createClientStub.mockRestore(); + registerErrorListenerSpy.mockRestore(); + connect$Stub.mockRestore(); + mergeCloseEvent.mockRestore(); }); describe('when is not connected', () => { beforeEach(async () => { @@ -284,13 +281,13 @@ describe('ClientMqtt', () => { await client.connect(); }); it('should call "registerErrorListener" once', async () => { - expect(registerErrorListenerSpy.called).to.be.true; + expect(registerErrorListenerSpy).toHaveBeenCalled(); }); it('should call "createClient" once', async () => { - expect(createClientStub.called).to.be.true; + expect(createClientStub).toHaveBeenCalled(); }); it('should call "connect$" once', async () => { - expect(connect$Stub.called).to.be.true; + expect(connect$Stub).toHaveBeenCalled(); }); }); describe('when is connected', () => { @@ -298,13 +295,13 @@ describe('ClientMqtt', () => { client['mqttClient'] = { test: true } as any; }); it('should not call "createClient"', () => { - expect(createClientStub.called).to.be.false; + expect(createClientStub).not.toHaveBeenCalled(); }); it('should not call "registerErrorListener"', () => { - expect(registerErrorListenerSpy.called).to.be.false; + expect(registerErrorListenerSpy).not.toHaveBeenCalled(); }); it('should not call "connect$"', () => { - expect(connect$Stub.called).to.be.false; + expect(connect$Stub).not.toHaveBeenCalled(); }); }); }); @@ -316,70 +313,80 @@ describe('ClientMqtt', () => { off: () => ({}), }; client.mergeCloseEvent(instance, EMPTY).subscribe({ - error: (err: any) => expect(err).to.be.eql(error), + error: (err: any) => expect(err).toEqual(error), }); }); }); describe('registerErrorListener', () => { it('should bind error event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerErrorListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(MqttEventsMap.ERROR); + expect(callback.mock.calls[0][0]).toEqual('error'); }); }); describe('registerConnectListener', () => { it('should bind connect event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerConnectListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(MqttEventsMap.CONNECT); + expect(callback.mock.calls[0][0]).toEqual('connect'); }); }); describe('registerDisconnectListener', () => { it('should bind disconnect event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerDisconnectListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(MqttEventsMap.DISCONNECT); + expect(callback.mock.calls[0][0]).toEqual('disconnect'); }); }); describe('registerOfflineListener', () => { it('should bind offline event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerOfflineListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(MqttEventsMap.OFFLINE); + expect(callback.mock.calls[0][0]).toEqual('offline'); }); }); describe('registerCloseListener', () => { it('should bind close event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerCloseListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(MqttEventsMap.CLOSE); + expect(callback.mock.calls[0][0]).toEqual('close'); }); }); describe('dispatchEvent', () => { let msg: ReadPacket; - let publishStub: sinon.SinonStub, mqttClient; + let publishStub: ReturnType, mqttClient; beforeEach(() => { client = new ClientMqtt({}); untypedClient = client as any; msg = { pattern: 'pattern', data: 'data' }; - publishStub = sinon.stub(); + publishStub = vi.fn(); mqttClient = { publish: publishStub, }; @@ -387,37 +394,37 @@ describe('ClientMqtt', () => { }); it('should publish packet', async () => { - publishStub.callsFake((a, b, c, d) => d()); + publishStub.mockImplementation((a, b, c, d) => d()); await client['dispatchEvent'](msg); - expect(publishStub.called).to.be.true; + expect(publishStub).toHaveBeenCalled(); }); it('should throw error', async () => { - publishStub.callsFake((a, b, c, d) => d(new Error())); + publishStub.mockImplementation((a, b, c, d) => d(new Error())); client['dispatchEvent'](msg).catch(err => - expect(err).to.be.instanceOf(Error), + expect(err).toBeInstanceOf(Error), ); }); describe('headers', () => { it('should not generate headers if none are configured', async () => { - publishStub.callsFake((a, b, c, d) => d()); + publishStub.mockImplementation((a, b, c, d) => d()); await client['dispatchEvent'](msg); - expect(publishStub.getCall(0).args[2]).to.be.undefined; + expect(publishStub.mock.calls[0][2]).toBeUndefined(); }); it('should send packet headers', async () => { - publishStub.callsFake((a, b, c, d) => d()); + publishStub.mockImplementation((a, b, c, d) => d()); const requestHeaders = { '1': '123' }; msg.data = new MqttRecord('data', { properties: { userProperties: requestHeaders }, }); await client['dispatchEvent'](msg); - expect(publishStub.getCall(0).args[2].properties.userProperties).to.eql( + expect(publishStub.mock.calls[0][2].properties.userProperties).toEqual( requestHeaders, ); }); it('should combine packet and static headers', async () => { - publishStub.callsFake((a, b, c, d) => d()); + publishStub.mockImplementation((a, b, c, d) => d()); const staticHeaders = { 'client-id': 'some-client-id' }; untypedClient.options.userProperties = staticHeaders; @@ -427,15 +434,13 @@ describe('ClientMqtt', () => { }); await client['dispatchEvent'](msg); - expect(publishStub.getCall(0).args[2].properties.userProperties).to.eql( - { - ...staticHeaders, - ...requestHeaders, - }, - ); + expect(publishStub.mock.calls[0][2].properties.userProperties).toEqual({ + ...staticHeaders, + ...requestHeaders, + }); }); it('should prefer packet headers over static headers', async () => { - publishStub.callsFake((a, b, c, d) => d()); + publishStub.mockImplementation((a, b, c, d) => d()); const staticHeaders = { 'client-id': 'some-client-id' }; untypedClient.options.headers = staticHeaders; @@ -445,7 +450,7 @@ describe('ClientMqtt', () => { }); await client['dispatchEvent'](msg); - expect(publishStub.getCall(0).args[2].properties.userProperties).to.eql( + expect(publishStub.mock.calls[0][2].properties.userProperties).toEqual( requestHeaders, ); }); diff --git a/packages/microservices/test/client/client-nats.spec.ts b/packages/microservices/test/client/client-nats.spec.ts index c818351fe36..f45377392f9 100644 --- a/packages/microservices/test/client/client-nats.spec.ts +++ b/packages/microservices/test/client/client-nats.spec.ts @@ -1,9 +1,7 @@ -import { expect } from 'chai'; -import { headers as createHeaders, JSONCodec } from 'nats'; -import * as sinon from 'sinon'; -import { ClientNats } from '../../client/client-nats'; -import { ReadPacket } from '../../interfaces'; -import { NatsRecord } from '../../record-builders'; +import { headers as createHeaders } from '@nats-io/transport-node'; +import { ClientNats } from '../../client/client-nats.js'; +import { ReadPacket, WritePacket } from '../../interfaces/index.js'; +import { NatsRecord } from '../../record-builders/index.js'; describe('ClientNats', () => { let client: ClientNats; @@ -14,27 +12,30 @@ describe('ClientNats', () => { const pattern = 'test'; const id = 3; - let subscribeSpy: sinon.SinonSpy, - publishSpy: sinon.SinonSpy, - removeListenerSpy: sinon.SinonSpy, - unsubscribeSpy: sinon.SinonSpy, - connectSpy: sinon.SinonStub, + let subscribeSpy: ReturnType, + publishSpy: ReturnType, + removeListenerSpy: ReturnType, + unsubscribeSpy: ReturnType, + connectSpy: ReturnType, natsClient: any, subscription: any, - createClient: sinon.SinonStub; + createClient: ReturnType; - beforeEach(() => { + beforeEach(async () => { client = new ClientNats({}); untypedClient = client as any; + // Resolve the nats package (loaded asynchronously in constructor) + await client.createClient().catch(() => {}); + msg = { pattern, data: 'data' }; - unsubscribeSpy = sinon.spy(); + unsubscribeSpy = vi.fn(); subscription = { unsubscribe: unsubscribeSpy, }; - subscribeSpy = sinon.spy(() => subscription); - publishSpy = sinon.spy(); - removeListenerSpy = sinon.spy(); + subscribeSpy = vi.fn().mockReturnValue(subscription); + publishSpy = vi.fn(); + removeListenerSpy = vi.fn(); natsClient = { subscribe: subscribeSpy, @@ -44,68 +45,73 @@ describe('ClientNats', () => { }; untypedClient.natsClient = natsClient; - connectSpy = sinon.stub(client, 'connect').callsFake(async () => { + connectSpy = vi.spyOn(client, 'connect').mockImplementation(async () => { untypedClient.natsClient = natsClient; }); - createClient = sinon - .stub(client, 'createClient') - .callsFake(() => untypedClient); + createClient = vi + .spyOn(client, 'createClient') + .mockImplementation(async () => untypedClient); }); afterEach(() => { - connectSpy.restore(); - createClient.restore(); + connectSpy.mockRestore(); + createClient.mockRestore(); }); it('should publish stringified message to pattern name', () => { client['publish'](msg, () => {}); - expect(publishSpy.getCall(0).args[0]).to.be.eql(pattern); + expect(publishSpy.mock.calls[0][0]).toEqual(pattern); }); describe('on error', () => { - let assignPacketIdStub: sinon.SinonStub; + let assignPacketIdStub: ReturnType; beforeEach(() => { - assignPacketIdStub = sinon - .stub(client, 'assignPacketId' as any) - .callsFake(() => { + assignPacketIdStub = vi + .spyOn(client, 'assignPacketId' as any) + .mockImplementation(() => { throw new Error(); }); }); afterEach(() => { - assignPacketIdStub.restore(); + assignPacketIdStub.mockRestore(); }); it('should call callback', () => { - const callback = sinon.spy(); + const callback = vi.fn(); client['publish'](msg, callback); - expect(callback.called).to.be.true; - expect(callback.getCall(0).args[0].err).to.be.instanceof(Error); + expect(callback).toHaveBeenCalled(); + expect(callback.mock.calls[0][0].err).toBeInstanceOf(Error); }); }); describe('dispose callback', () => { - let assignStub: sinon.SinonStub; - let callback: sinon.SinonSpy, subscription; + let assignStub: ReturnType; + let callback: ReturnType, subscription; beforeEach(async () => { - callback = sinon.spy(); - assignStub = sinon - .stub(client, 'assignPacketId' as any) - .callsFake(packet => Object.assign(packet as object, { id })); - - subscription = client['publish'](msg, callback); + callback = vi.fn(); + assignStub = vi + .spyOn(client, 'assignPacketId' as any) + .mockImplementation(packet => + Object.assign(packet as object, { id }), + ); + + subscription = client['publish']( + msg, + callback as (packet: WritePacket) => any, + ); subscription(); }); afterEach(() => { - assignStub.restore(); + assignStub.mockRestore(); }); it('should unsubscribe', () => { - expect(unsubscribeSpy.called).to.be.true; + expect(unsubscribeSpy).toHaveBeenCalled(); }); }); describe('headers', () => { it('should not generate headers if none are configured', () => { client['publish'](msg, () => {}); - expect(natsClient.publish.getCall(0).args[2].headers).to.be.undefined; + expect(natsClient.publish.mock.calls[0][2].headers).toBeUndefined(); }); it('should send packet headers', () => { @@ -114,7 +120,7 @@ describe('ClientNats', () => { msg.data = new NatsRecord('data', requestHeaders); client['publish'](msg, () => {}); - expect(natsClient.publish.getCall(0).args[2].headers.get('1')).to.eql( + expect(natsClient.publish.mock.calls[0][2].headers.get('1')).toEqual( '123', ); }); @@ -127,10 +133,10 @@ describe('ClientNats', () => { msg.data = new NatsRecord('data', requestHeaders); client['publish'](msg, () => {}); - expect(publishSpy.getCall(0).args[2].headers.get('client-id')).to.eql( + expect(publishSpy.mock.calls[0][2].headers.get('client-id')).toEqual( 'some-client-id', ); - expect(publishSpy.getCall(0).args[2].headers.get('1')).to.eql('123'); + expect(publishSpy.mock.calls[0][2].headers.get('1')).toEqual('123'); }); it('should prefer packet headers over static headers', () => { @@ -142,7 +148,7 @@ describe('ClientNats', () => { msg.data = new NatsRecord('data', requestHeaders); client['publish'](msg, () => {}); - expect(publishSpy.getCall(0).args[2].headers.get('client-id')).to.eql( + expect(publishSpy.mock.calls[0][2].headers.get('client-id')).toEqual( 'override-client-id', ); }); @@ -157,102 +163,110 @@ describe('ClientNats', () => { id: '1', }; const natsMessage = { - data: JSONCodec().encode(responseMessage), + data: JSON.stringify(responseMessage), + json: () => responseMessage, }; - let callback: sinon.SinonSpy, subscription; + let callback: ReturnType, subscription; describe('not completed', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); - subscription = client.createSubscriptionHandler(msg, callback); - subscription(undefined, natsMessage); + subscription = client.createSubscriptionHandler( + msg, + callback as (packet: WritePacket) => any, + ); + await subscription(undefined, natsMessage); }); it('should call callback with expected arguments', () => { - expect( - callback.calledWith({ - err: undefined, - response: responseMessage.response, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: undefined, + response: responseMessage.response, + }); }); }); describe('disposed and "id" is correct', () => { beforeEach(async () => { - callback = sinon.spy(); - subscription = client.createSubscriptionHandler(msg, callback); + callback = vi.fn(); + subscription = client.createSubscriptionHandler( + msg, + callback as (packet: WritePacket) => any, + ); subscription(undefined, { - data: JSONCodec().encode({ + data: JSON.stringify({ ...responseMessage, isDisposed: true, }), + json: function () { + return JSON.parse(this.data); + }, }); }); it('should call callback with dispose param', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - isDisposed: true, - response: responseMessage.response, - err: undefined, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + isDisposed: true, + response: responseMessage.response, + err: undefined, + }); }); }); describe('disposed and "id" is incorrect', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createSubscriptionHandler( { ...msg, id: '2', }, - callback, + callback as (packet: WritePacket) => any, ); subscription(undefined, { - data: JSONCodec().encode({ + data: JSON.stringify({ ...responseMessage, isDisposed: true, }), + json: function () { + return JSON.parse(this.data); + }, }); }); it('should not call callback', () => { - expect(callback.called).to.be.false; + expect(callback).not.toHaveBeenCalled(); }); }); }); describe('close', () => { - let natsClose: sinon.SinonSpy; + let natsClose: ReturnType; let natsClient: any; beforeEach(() => { - natsClose = sinon.spy(); + natsClose = vi.fn(); natsClient = { close: natsClose }; untypedClient.natsClient = natsClient; }); it('should close "natsClient" when it is not null', async () => { await client.close(); - expect(natsClose.called).to.be.true; + expect(natsClose).toHaveBeenCalled(); }); }); describe('connect', () => { - let createClientSpy: sinon.SinonSpy; - let handleStatusUpdatesSpy: sinon.SinonSpy; + let createClientSpy: ReturnType; + let handleStatusUpdatesSpy: ReturnType; beforeEach(async () => { - createClientSpy = sinon - .stub(client, 'createClient') - .callsFake(() => Promise.resolve({})); - handleStatusUpdatesSpy = sinon.spy(client, 'handleStatusUpdates'); + createClientSpy = vi + .spyOn(client, 'createClient') + .mockImplementation(() => Promise.resolve({})); + handleStatusUpdatesSpy = vi.spyOn(client, 'handleStatusUpdates'); await client.connect(); }); afterEach(() => { - createClientSpy.restore(); - handleStatusUpdatesSpy.restore(); + createClientSpy.mockRestore(); + handleStatusUpdatesSpy.mockRestore(); }); describe('when is not connected', () => { beforeEach(async () => { @@ -261,10 +275,10 @@ describe('ClientNats', () => { await client.connect(); }); it('should call "handleStatusUpdatesSpy" once', async () => { - expect(handleStatusUpdatesSpy.called).to.be.true; + expect(handleStatusUpdatesSpy).toHaveBeenCalled(); }); it('should call "createClient" once', async () => { - expect(createClientSpy.called).to.be.true; + expect(createClientSpy).toHaveBeenCalled(); }); }); describe('when is connected', () => { @@ -273,49 +287,51 @@ describe('ClientNats', () => { client['connection'] = Promise.resolve(true); }); it('should not call "createClient"', () => { - expect(createClientSpy.called).to.be.false; + expect(createClientSpy).not.toHaveBeenCalled(); }); it('should not call "handleStatusUpdatesSpy"', () => { - expect(handleStatusUpdatesSpy.called).to.be.false; + expect(handleStatusUpdatesSpy).not.toHaveBeenCalled(); }); }); }); describe('handleStatusUpdates', () => { it('should retrieve "status()" async iterator', () => { const clientMock = { - status: sinon.stub().returns({ - [Symbol.asyncIterator]: [], + status: vi.fn().mockReturnValue({ + async *[Symbol.asyncIterator]() { + // empty iterator + }, }), }; void client.handleStatusUpdates(clientMock as any); - expect(clientMock.status.called).to.be.true; + expect(clientMock.status).toHaveBeenCalled(); }); it('should log "disconnect" and "error" statuses as "errors"', async () => { - const logErrorSpy = sinon.spy(untypedClient.logger, 'error'); + const logErrorSpy = vi.spyOn(untypedClient.logger, 'error'); const clientMock = { - status: sinon.stub().returns({ + status: vi.fn().mockReturnValue({ async *[Symbol.asyncIterator]() { - yield { type: 'disconnect', data: 'localhost' }; - yield { type: 'error', data: {} }; + yield { type: 'disconnect' }; + yield { type: 'error', error: 'Test error' }; }, }), }; await client.handleStatusUpdates(clientMock as any); - expect(logErrorSpy.calledTwice).to.be.true; - expect( - logErrorSpy.calledWith( - `NatsError: type: "disconnect", data: "localhost".`, - ), + expect(logErrorSpy).toHaveBeenCalledTimes(2); + expect(logErrorSpy).toHaveBeenNthCalledWith( + 1, + 'NatsError: type: "disconnect".', ); - expect( - logErrorSpy.calledWith(`NatsError: type: "disconnect", data: "{}".`), + expect(logErrorSpy).toHaveBeenNthCalledWith( + 2, + 'NatsError: type: "error", error: "Test error".', ); }); it('should log other statuses as "logs"', async () => { - const logSpy = sinon.spy(untypedClient.logger, 'log'); + const logSpy = vi.spyOn(untypedClient.logger, 'log'); const clientMock = { - status: sinon.stub().returns({ + status: vi.fn().mockReturnValue({ async *[Symbol.asyncIterator]() { yield { type: 'non-disconnect', data: 'localhost' }; yield { type: 'warn', data: {} }; @@ -323,29 +339,32 @@ describe('ClientNats', () => { }), }; await client.handleStatusUpdates(clientMock as any); - expect(logSpy.calledTwice).to.be.true; - expect( - logSpy.calledWith( - `NatsStatus: type: "non-disconnect", data: "localhost".`, - ), + expect(logSpy).toHaveBeenCalledTimes(2); + expect(logSpy).toHaveBeenCalledWith( + 'NatsStatus: type: "non-disconnect", data: "localhost".', + ); + expect(logSpy).toHaveBeenCalledWith( + 'NatsStatus: type: "warn", data: "{}".', ); - expect(logSpy.calledWith(`NatsStatus: type: "warn", data: "{}".`)); }); }); describe('dispatchEvent', () => { let msg: ReadPacket; - let subscribeStub: sinon.SinonStub, natsClient: any; + let subscribeStub: ReturnType, natsClient: any; - beforeEach(() => { + beforeEach(async () => { client = new ClientNats({}); untypedClient = client as any; + // Resolve the nats package (loaded asynchronously in constructor) + await client.createClient().catch(() => {}); + msg = { pattern: 'pattern', data: 'data' }; - subscribeStub = sinon - .stub() - .callsFake((channel, options) => options.callback()); + subscribeStub = vi + .fn() + .mockImplementation((channel, options) => options.callback()); natsClient = { - publish: sinon.spy(), + publish: vi.fn(), subscribe: subscribeStub, }; untypedClient.natsClient = natsClient; @@ -354,22 +373,22 @@ describe('ClientNats', () => { it('should publish packet', async () => { await client['dispatchEvent'](msg); - expect(natsClient.publish.called).to.be.true; + expect(natsClient.publish).toHaveBeenCalled(); }); it('should throw error', async () => { - subscribeStub.callsFake((channel, options) => + subscribeStub.mockImplementation((channel, options) => options.callback(new Error()), ); await client['dispatchEvent'](msg).catch(err => - expect(err).to.be.instanceOf(Error), + expect(err).toBeInstanceOf(Error), ); }); describe('headers', () => { it('should not generate headers if none are configured', async () => { await client['dispatchEvent'](msg); - expect(natsClient.publish.getCall(0).args[2].headers).to.be.undefined; + expect(natsClient.publish.mock.calls[0][2].headers).toBeUndefined(); }); it('should send packet headers', async () => { @@ -378,7 +397,7 @@ describe('ClientNats', () => { msg.data = new NatsRecord('data', requestHeaders); await client['dispatchEvent'](msg); - expect(natsClient.publish.getCall(0).args[2].headers.get('1')).to.eql( + expect(natsClient.publish.mock.calls[0][2].headers.get('1')).toEqual( '123', ); }); @@ -393,9 +412,9 @@ describe('ClientNats', () => { await client['dispatchEvent'](msg); expect( - natsClient.publish.getCall(0).args[2].headers.get('client-id'), - ).to.eql('some-client-id'); - expect(natsClient.publish.getCall(0).args[2].headers.get('1')).to.eql( + natsClient.publish.mock.calls[0][2].headers.get('client-id'), + ).toEqual('some-client-id'); + expect(natsClient.publish.mock.calls[0][2].headers.get('1')).toEqual( '123', ); }); @@ -410,8 +429,8 @@ describe('ClientNats', () => { await client['dispatchEvent'](msg); expect( - natsClient.publish.getCall(0).args[2].headers.get('client-id'), - ).to.eql('override-client-id'); + natsClient.publish.mock.calls[0][2].headers.get('client-id'), + ).toEqual('override-client-id'); }); }); }); diff --git a/packages/microservices/test/client/client-proxy-factory.spec.ts b/packages/microservices/test/client/client-proxy-factory.spec.ts index 2bc656f0ed2..ee60ea97a5a 100644 --- a/packages/microservices/test/client/client-proxy-factory.spec.ts +++ b/packages/microservices/test/client/client-proxy-factory.spec.ts @@ -1,56 +1,55 @@ -import { expect } from 'chai'; -import { ClientProxyFactory } from '../../client/client-proxy-factory'; -import { ClientTCP } from '../../client/client-tcp'; -import { Transport } from '../../enums/transport.enum'; -import { ClientRedis } from '../../client/client-redis'; -import { ClientNats } from '../../client/client-nats'; -import { ClientMqtt } from '../../client/client-mqtt'; -import { ClientGrpcProxy } from '../../client/client-grpc'; -import { ClientRMQ } from '../../client/client-rmq'; -import { ClientKafka } from '../../client/client-kafka'; import { join } from 'path'; +import { ClientGrpcProxy } from '../../client/client-grpc.js'; +import { ClientKafka } from '../../client/client-kafka.js'; +import { ClientMqtt } from '../../client/client-mqtt.js'; +import { ClientNats } from '../../client/client-nats.js'; +import { ClientProxyFactory } from '../../client/client-proxy-factory.js'; +import { ClientRedis } from '../../client/client-redis.js'; +import { ClientRMQ } from '../../client/client-rmq.js'; +import { ClientTCP } from '../../client/client-tcp.js'; +import { Transport } from '../../enums/transport.enum.js'; describe('ClientProxyFactory', () => { describe('create', () => { it(`should create tcp client by default`, () => { const proxy = ClientProxyFactory.create({}); - expect(proxy instanceof ClientTCP).to.be.true; + expect(proxy instanceof ClientTCP).toBe(true); }); it(`should create redis client`, () => { const proxy = ClientProxyFactory.create({ transport: Transport.REDIS }); - expect(proxy instanceof ClientRedis).to.be.true; + expect(proxy instanceof ClientRedis).toBe(true); }); it(`should create nats client`, () => { const proxy = ClientProxyFactory.create({ transport: Transport.NATS }); - expect(proxy instanceof ClientNats).to.be.true; + expect(proxy instanceof ClientNats).toBe(true); }); it(`should create mqtt client`, () => { const proxy = ClientProxyFactory.create({ transport: Transport.MQTT }); - expect(proxy instanceof ClientMqtt).to.be.true; + expect(proxy instanceof ClientMqtt).toBe(true); }); it(`should create grpc client`, () => { const proxy = ClientProxyFactory.create({ transport: Transport.GRPC, options: { - protoPath: join(__dirname, './test.proto'), + protoPath: join(import.meta.dirname, './test.proto'), package: 'test', }, }); - expect(proxy instanceof ClientGrpcProxy).to.be.true; + expect(proxy instanceof ClientGrpcProxy).toBe(true); }); it(`should create rmq client`, () => { const proxy = ClientProxyFactory.create({ transport: Transport.RMQ }); - expect(proxy instanceof ClientRMQ).to.be.true; + expect(proxy instanceof ClientRMQ).toBe(true); }); it(`should create kafka client`, () => { const proxy = ClientProxyFactory.create({ transport: Transport.KAFKA }); - expect(proxy instanceof ClientKafka).to.be.true; + expect(proxy instanceof ClientKafka).toBe(true); }); }); }); diff --git a/packages/microservices/test/client/client-proxy.spec.ts b/packages/microservices/test/client/client-proxy.spec.ts index e0afccdb036..10fc2715de5 100644 --- a/packages/microservices/test/client/client-proxy.spec.ts +++ b/packages/microservices/test/client/client-proxy.spec.ts @@ -1,8 +1,6 @@ -import { expect } from 'chai'; import { Observable } from 'rxjs'; -import * as sinon from 'sinon'; -import { ClientProxy } from '../../client/client-proxy'; -import { ReadPacket } from '../../interfaces'; +import { ClientProxy } from '../../client/client-proxy.js'; +import { ReadPacket } from '../../interfaces/index.js'; class TestClientProxy extends ClientProxy { protected async dispatchEvent( @@ -22,8 +20,6 @@ class TestClientProxy extends ClientProxy { } describe('ClientProxy', function () { - this.retries(10); - let client: TestClientProxy; beforeEach(() => { client = new TestClientProxy(); @@ -34,9 +30,9 @@ describe('ClientProxy', function () { it(`"error" when first parameter is not null or undefined`, () => { const testClient = new TestClientProxy(); const err = 'test'; - const error = sinon.spy(); - const next = sinon.spy(); - const complete = sinon.spy(); + const error = vi.fn(); + const next = vi.fn(); + const complete = vi.fn(); const observer = { error, next, @@ -45,15 +41,15 @@ describe('ClientProxy', function () { const fn = testClient['createObserver'](observer); fn({ err }); - expect(error.calledWith(err)).to.be.true; + expect(error).toHaveBeenCalledWith(err); }); it(`"next" when first parameter is null or undefined`, () => { const testClient = new TestClientProxy(); const data = 'test'; - const error = sinon.spy(); - const next = sinon.spy(); - const complete = sinon.spy(); + const error = vi.fn(); + const next = vi.fn(); + const complete = vi.fn(); const observer = { error, next, @@ -62,15 +58,15 @@ describe('ClientProxy', function () { const fn = testClient['createObserver'](observer); fn({ response: data }); - expect(next.calledWith(data)).to.be.true; + expect(next).toHaveBeenCalledWith(data); }); it(`"complete" when third parameter is true`, () => { const testClient = new TestClientProxy(); const data = 'test'; - const error = sinon.spy(); - const next = sinon.spy(); - const complete = sinon.spy(); + const error = vi.fn(); + const next = vi.fn(); + const complete = vi.fn(); const observer = { error, next, @@ -79,7 +75,7 @@ describe('ClientProxy', function () { const fn = testClient['createObserver'](observer); fn({ data, isDisposed: true } as any); - expect(complete.called).to.be.true; + expect(complete).toHaveBeenCalled(); }); }); }); @@ -87,96 +83,236 @@ describe('ClientProxy', function () { describe('send', () => { it(`should return an observable stream`, () => { const stream$ = client.send({}, ''); - expect(stream$ instanceof Observable).to.be.true; + expect(stream$ instanceof Observable).toBe(true); }); it('should call "connect" on subscribe', () => { - const connectSpy = sinon.spy(client, 'connect'); + const connectSpy = vi.spyOn(client, 'connect'); const stream$ = client.send({ test: 3 }, 'test'); stream$.subscribe(); - expect(connectSpy.calledOnce).to.be.true; + expect(connectSpy).toHaveBeenCalledOnce(); }); describe('when "connect" throws', () => { it('should return Observable with error', () => { - sinon.stub(client, 'connect').callsFake(() => { + vi.spyOn(client, 'connect').mockImplementation(() => { throw new Error(); }); const stream$ = client.send({ test: 3 }, 'test'); stream$.subscribe({ next: () => {}, error: err => { - expect(err).to.be.instanceof(Error); + expect(err).toBeInstanceOf(Error); }, }); }); }); describe('when is connected', () => { beforeEach(() => { - sinon.stub(client, 'connect').callsFake(() => Promise.resolve()); + vi.spyOn(client, 'connect').mockImplementation(() => Promise.resolve()); }); it(`should call "publish"`, () => { const pattern = { test: 3 }; const data = 'test'; - const publishSpy = sinon.spy(); + const publishSpy = vi.fn(); const stream$ = client.send(pattern, data); client.publish = publishSpy; stream$.subscribe(() => { - expect(publishSpy.calledOnce).to.be.true; + expect(publishSpy).toHaveBeenCalledOnce(); }); }); }); it('should return Observable with error', () => { const err$ = client.send(null, null); - expect(err$).to.be.instanceOf(Observable); + expect(err$).toBeInstanceOf(Observable); }); }); describe('emit', () => { it(`should return an observable stream`, () => { const stream$ = client.emit({}, ''); - expect(stream$ instanceof Observable).to.be.true; + expect(stream$ instanceof Observable).toBe(true); }); it('should call "connect" immediately', () => { - const connectSpy = sinon.spy(client, 'connect'); + const connectSpy = vi.spyOn(client, 'connect'); client.emit({ test: 3 }, 'test'); - expect(connectSpy.calledOnce).to.be.true; + expect(connectSpy).toHaveBeenCalledOnce(); }); describe('when "connect" throws', () => { it('should return Observable with error', () => { - sinon.stub(client, 'connect').callsFake(() => { + vi.spyOn(client, 'connect').mockImplementation(() => { throw new Error(); }); const stream$ = client.emit({ test: 3 }, 'test'); stream$.subscribe({ next: () => {}, error: err => { - expect(err).to.be.instanceof(Error); + expect(err).toBeInstanceOf(Error); }, }); }); }); describe('when is connected', () => { beforeEach(() => { - sinon.stub(client, 'connect').callsFake(() => Promise.resolve()); + vi.spyOn(client, 'connect').mockImplementation(() => Promise.resolve()); }); it(`should call "dispatchEvent"`, () => { const pattern = { test: 3 }; const data = 'test'; - const dispatchEventSpy = sinon - .stub() - .callsFake(() => Promise.resolve(true)); + const dispatchEventSpy = vi + .fn() + .mockImplementation(() => Promise.resolve(true)); const stream$ = client.emit(pattern, data); client['dispatchEvent'] = dispatchEventSpy; stream$.subscribe(() => { - expect(dispatchEventSpy.calledOnce).to.be.true; + expect(dispatchEventSpy).toHaveBeenCalledOnce(); }); }); }); it('should return Observable with error', () => { const err$ = client.emit(null, null); - expect(err$).to.be.instanceOf(Observable); + expect(err$).toBeInstanceOf(Observable); + }); + }); + + describe('createObserver', () => { + it('should call next then complete when response is defined and isDisposed is true', () => { + const next = vi.fn(); + const complete = vi.fn(); + const error = vi.fn(); + const fn = client['createObserver']({ next, complete, error } as any); + fn({ response: 'data', isDisposed: true }); + expect(next).toHaveBeenCalledWith('data'); + expect(complete).toHaveBeenCalled(); + expect(error).not.toHaveBeenCalled(); + }); + + it('should call only complete when response is undefined and isDisposed is true', () => { + const next = vi.fn(); + const complete = vi.fn(); + const error = vi.fn(); + const fn = client['createObserver']({ next, complete, error } as any); + fn({ isDisposed: true }); + expect(next).not.toHaveBeenCalled(); + expect(complete).toHaveBeenCalled(); + }); + }); + + describe('serializeError', () => { + it('should return error as-is (identity)', () => { + const err = new Error('test'); + expect(client['serializeError'](err)).toBe(err); + }); + }); + + describe('serializeResponse', () => { + it('should return response as-is (identity)', () => { + const response = { data: 42 }; + expect(client['serializeResponse'](response)).toBe(response); + }); + }); + + describe('assignPacketId', () => { + it('should add an id property to the packet', () => { + const packet = { pattern: 'test', data: {} }; + const result = client['assignPacketId'](packet); + expect(result.id).toBeDefined(); + expect(typeof result.id).toBe('string'); + expect(result.pattern).toBe('test'); + }); + }); + + describe('normalizePattern', () => { + it('should transform object pattern to route string', () => { + const result = client['normalizePattern']({ cmd: 'test' }); + expect(typeof result).toBe('string'); + expect(result).toContain('test'); + }); + + it('should pass string pattern through', () => { + expect(client['normalizePattern']('my-pattern')).toBe('my-pattern'); + }); + }); + + describe('initializeSerializer', () => { + it('should use IdentitySerializer when no serializer option', () => { + client['initializeSerializer']({} as any); + expect(client['serializer']).toBeDefined(); + }); + + it('should use IdentitySerializer when options is undefined', () => { + client['initializeSerializer'](undefined as any); + expect(client['serializer']).toBeDefined(); + }); + + it('should use provided serializer from options', () => { + const customSerializer = { serialize: vi.fn() }; + client['initializeSerializer']({ serializer: customSerializer } as any); + expect(client['serializer']).toBe(customSerializer); + }); + }); + + describe('initializeDeserializer', () => { + it('should use IncomingResponseDeserializer when no deserializer option', () => { + client['initializeDeserializer']({} as any); + expect(client['deserializer']).toBeDefined(); + }); + + it('should use IncomingResponseDeserializer when options is undefined', () => { + client['initializeDeserializer'](undefined as any); + expect(client['deserializer']).toBeDefined(); + }); + + it('should use provided deserializer from options', () => { + const customDeserializer = { deserialize: vi.fn() }; + client['initializeDeserializer']({ + deserializer: customDeserializer, + } as any); + expect(client['deserializer']).toBe(customDeserializer); + }); + }); + + describe('getOptionsProp', () => { + it('should return value when prop exists', () => { + const options = { host: 'localhost', port: 3000 }; + expect(client['getOptionsProp'](options as any, 'host' as any)).toBe( + 'localhost', + ); + }); + + it('should return defaultValue when prop does not exist', () => { + const options = {} as any; + expect( + client['getOptionsProp'](options, 'host' as any, 'default-host'), + ).toBe('default-host'); + }); + + it('should return undefined defaultValue when obj is falsy', () => { + expect( + client['getOptionsProp'](undefined as any, 'host' as any, 'fallback'), + ).toBe('fallback'); + }); + }); + + describe('status', () => { + it('should return an observable', () => { + expect(client.status).toBeDefined(); + expect(client.status.subscribe).toBeDefined(); + }); + }); + + describe('on', () => { + it('should throw "Method not implemented." error', () => { + expect(() => client.on('event' as any, (() => {}) as any)).toThrow( + 'Method not implemented.', + ); + }); + }); + + describe('routingMap', () => { + it('should be a Map instance', () => { + expect(client['routingMap']).toBeInstanceOf(Map); }); }); }); diff --git a/packages/microservices/test/client/client-redis.spec.ts b/packages/microservices/test/client/client-redis.spec.ts index 17486764ff6..82a46496813 100644 --- a/packages/microservices/test/client/client-redis.spec.ts +++ b/packages/microservices/test/client/client-redis.spec.ts @@ -1,7 +1,4 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ClientRedis } from '../../client/client-redis'; -import { RedisEventsMap } from '../../events/redis.events'; +import { ClientRedis } from '../../client/client-redis.js'; describe('ClientRedis', () => { const test = 'test'; @@ -11,33 +8,33 @@ describe('ClientRedis', () => { describe('getRequestPattern', () => { it(`should leave pattern as it is`, () => { const expectedResult = test; - expect(client.getRequestPattern(test)).to.equal(expectedResult); + expect(client.getRequestPattern(test)).toBe(expectedResult); }); }); describe('getReplyPattern', () => { it(`should append ".reply" to string`, () => { const expectedResult = test + '.reply'; - expect(client.getReplyPattern(test)).to.equal(expectedResult); + expect(client.getReplyPattern(test)).toBe(expectedResult); }); }); describe('publish', () => { const pattern = 'test'; const msg = { pattern, data: 'data' }; - let subscribeSpy: sinon.SinonSpy, - publishSpy: sinon.SinonSpy, - onSpy: sinon.SinonSpy, - removeListenerSpy: sinon.SinonSpy, - unsubscribeSpy: sinon.SinonSpy, - connectSpy: sinon.SinonSpy, + let subscribeSpy: ReturnType, + publishSpy: ReturnType, + onSpy: ReturnType, + removeListenerSpy: ReturnType, + unsubscribeSpy: ReturnType, + connectSpy: ReturnType, sub: Record, pub: Record; beforeEach(() => { - subscribeSpy = sinon.spy((name, fn) => fn()); - publishSpy = sinon.spy(); - onSpy = sinon.spy(); - removeListenerSpy = sinon.spy(); - unsubscribeSpy = sinon.spy(); + subscribeSpy = vi.fn((name, fn) => fn()); + publishSpy = vi.fn(); + onSpy = vi.fn(); + removeListenerSpy = vi.fn(); + unsubscribeSpy = vi.fn(); sub = { subscribe: subscribeSpy, @@ -48,74 +45,79 @@ describe('ClientRedis', () => { pub = { publish: publishSpy }; untypedClient.subClient = sub; untypedClient.pubClient = pub; - connectSpy = sinon.spy(client, 'connect'); + untypedClient.connectionPromise = Promise.resolve(); + connectSpy = vi.spyOn(client, 'connect'); }); afterEach(() => { - connectSpy.restore(); + connectSpy.mockRestore(); + untypedClient.connectionPromise = null; }); it('should subscribe to response pattern name', () => { client['publish'](msg, () => {}); - expect(subscribeSpy.calledWith(`${pattern}.reply`)).to.be.true; + expect(subscribeSpy.mock.calls[0][0]).toEqual(`${pattern}.reply`); }); it('should publish stringified message to request pattern name', () => { client['publish'](msg, () => {}); - expect(publishSpy.calledWith(pattern, JSON.stringify(msg))).to.be.true; + expect(publishSpy).toHaveBeenCalledWith(pattern, JSON.stringify(msg)); }); describe('on error', () => { - let assignPacketIdStub: sinon.SinonStub; + let assignPacketIdStub: ReturnType; beforeEach(() => { - assignPacketIdStub = sinon - .stub(client, 'assignPacketId' as any) - .callsFake(() => { + assignPacketIdStub = vi + .spyOn(client, 'assignPacketId' as any) + .mockImplementation(() => { throw new Error(); }); }); afterEach(() => { - assignPacketIdStub.restore(); + assignPacketIdStub.mockRestore(); }); it('should call callback', () => { - const callback = sinon.spy(); + const callback = vi.fn(); client['publish'](msg, callback); - expect(callback.called).to.be.true; - expect(callback.getCall(0).args[0].err).to.be.instanceof(Error); + expect(callback).toHaveBeenCalled(); + expect(callback.mock.calls[0][0].err).toBeInstanceOf(Error); }); }); describe('dispose callback', () => { - let assignStub: sinon.SinonStub, getReplyPatternStub: sinon.SinonStub; - let callback: sinon.SinonSpy, subscription; + let assignStub: ReturnType, + getReplyPatternStub: ReturnType; + let callback: ReturnType, subscription; const channel = 'channel'; const id = '1'; beforeEach(async () => { - callback = sinon.spy(); - assignStub = sinon - .stub(client, 'assignPacketId' as any) - .callsFake(packet => Object.assign(packet as object, { id })); + callback = vi.fn(); + assignStub = vi + .spyOn(client, 'assignPacketId' as any) + .mockImplementation(packet => + Object.assign(packet as object, { id }), + ); - getReplyPatternStub = sinon - .stub(client, 'getReplyPattern') - .callsFake(() => channel); + getReplyPatternStub = vi + .spyOn(client, 'getReplyPattern') + .mockImplementation(() => channel); subscription = client['publish'](msg, callback); subscription(channel, JSON.stringify({ isDisposed: true, id })); }); afterEach(() => { - assignStub.restore(); - getReplyPatternStub.restore(); + assignStub.mockRestore(); + getReplyPatternStub.mockRestore(); }); it('should unsubscribe to response pattern name', () => { - expect(unsubscribeSpy.calledWith(channel)).to.be.true; + expect(unsubscribeSpy).toHaveBeenCalledWith(channel); }); it('should clean routingMap', () => { - expect(client['routingMap'].has(id)).to.be.false; + expect(client['routingMap'].has(id)).toBe(false); }); }); }); describe('createResponseCallback', () => { - let callback: sinon.SinonSpy, subscription; // : ReturnType; + let callback: ReturnType, subscription; // : ReturnType; const responseMessage = { response: 'test', id: '1', @@ -123,7 +125,7 @@ describe('ClientRedis', () => { describe('not completed', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createResponseCallback(); client['routingMap'].set(responseMessage.id, callback); @@ -133,17 +135,15 @@ describe('ClientRedis', () => { ); }); it('should call callback with expected arguments', () => { - expect( - callback.calledWith({ - err: undefined, - response: responseMessage.response, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: undefined, + response: responseMessage.response, + }); }); }); describe('disposed and "id" is correct', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createResponseCallback(); client['routingMap'].set(responseMessage.id, callback); subscription( @@ -158,38 +158,35 @@ describe('ClientRedis', () => { }); it('should call callback with dispose param', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - isDisposed: true, - response: responseMessage.response, - err: undefined, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + isDisposed: true, + response: responseMessage.response, + err: undefined, + }); }); }); describe('disposed and "id" is incorrect', () => { beforeEach(() => { - callback = sinon.spy(); + callback = vi.fn(); subscription = client.createResponseCallback(); subscription('channel', Buffer.from(JSON.stringify(responseMessage))); }); it('should not call callback', () => { - expect(callback.called).to.be.false; + expect(callback).not.toHaveBeenCalled(); }); }); }); describe('close', () => { const untypedClient = client as any; - let pubClose: sinon.SinonSpy; - let subClose: sinon.SinonSpy; + let pubClose: ReturnType; + let subClose: ReturnType; let pub: any, sub: any; beforeEach(() => { - pubClose = sinon.spy(); - subClose = sinon.spy(); + pubClose = vi.fn(); + subClose = vi.fn(); pub = { quit: pubClose }; sub = { quit: subClose }; untypedClient.pubClient = pub; @@ -197,21 +194,21 @@ describe('ClientRedis', () => { }); it('should close "pub" when it is not null', async () => { await client.close(); - expect(pubClose.called).to.be.true; + expect(pubClose).toHaveBeenCalled(); }); it('should not close "pub" when it is null', async () => { untypedClient.pubClient = null; await client.close(); - expect(pubClose.called).to.be.false; + expect(pubClose).not.toHaveBeenCalled(); }); it('should close "sub" when it is not null', async () => { await client.close(); - expect(subClose.called).to.be.true; + expect(subClose).toHaveBeenCalled(); }); it('should not close "sub" when it is null', async () => { untypedClient.subClient = null; await client.close(); - expect(subClose.called).to.be.false; + expect(subClose).not.toHaveBeenCalled(); }); it('should have isManuallyClosed set to true when "end" event is handled during close', async () => { let endHandler: Function | undefined; @@ -221,7 +218,7 @@ describe('ClientRedis', () => { sub.quit = async () => { if (endHandler) { endHandler(); - expect(untypedClient.isManuallyClosed).to.be.true; + expect(untypedClient.isManuallyClosed).toBe(true); } }; client.registerEndListener(sub); @@ -230,7 +227,7 @@ describe('ClientRedis', () => { it('should not log error when "end" event is handled during close', async () => { let endHandler: Function | undefined; - const logError = sinon.spy(untypedClient.logger, 'error'); + const logError = vi.spyOn(untypedClient.logger, 'error'); sub.on = (event, handler) => { if (event === 'end') endHandler = handler; }; @@ -241,15 +238,16 @@ describe('ClientRedis', () => { }; client.registerEndListener(sub); await client.close(); - expect(logError.called).to.be.false; + expect(logError).not.toHaveBeenCalled(); }); }); describe('connect', () => { - let createClientSpy: sinon.SinonSpy; - let registerErrorListenerSpy: sinon.SinonSpy; + let createClientSpy: ReturnType; + let registerErrorListenerSpy: ReturnType; beforeEach(async () => { - createClientSpy = sinon.stub(client, 'createClient').callsFake( + untypedClient.connectionPromise = null; + createClientSpy = vi.spyOn(client, 'createClient').mockImplementation( () => ({ on: () => null, @@ -258,74 +256,80 @@ describe('ClientRedis', () => { connect: () => Promise.resolve(), }) as any, ); - registerErrorListenerSpy = sinon.spy(client, 'registerErrorListener'); + registerErrorListenerSpy = vi.spyOn(client, 'registerErrorListener'); await client.connect(); client['pubClient'] = null; }); afterEach(() => { - createClientSpy.restore(); - registerErrorListenerSpy.restore(); + createClientSpy.mockRestore(); + registerErrorListenerSpy.mockRestore(); }); it('should call "createClient" twice', () => { - expect(createClientSpy.calledTwice).to.be.true; + expect(createClientSpy).toHaveBeenCalledTimes(2); }); it('should call "registerErrorListener" twice', () => { - expect(registerErrorListenerSpy.calledTwice).to.be.true; + expect(registerErrorListenerSpy).toHaveBeenCalledTimes(2); }); }); describe('registerErrorListener', () => { it('should bind error event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { addListener: callback, }; client.registerErrorListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(RedisEventsMap.ERROR); + expect(callback.mock.calls[0][0]).toEqual('error'); }); }); describe('registerEndListener', () => { it('should bind end event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerEndListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(RedisEventsMap.END); + expect(callback.mock.calls[0][0]).toEqual('end'); }); }); describe('registerReadyListener', () => { it('should bind ready event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerReadyListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(RedisEventsMap.READY); + expect(callback.mock.calls[0][0]).toEqual('ready'); }); }); describe('registerReconnectListener', () => { it('should bind reconnect event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerReconnectListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql( - RedisEventsMap.RECONNECTING, - ); + expect(callback.mock.calls[0][0]).toEqual('reconnecting'); }); }); describe('getClientOptions', () => { it('should return options object with "retryStrategy" and call "createRetryStrategy"', () => { - const createSpy = sinon.spy(client, 'createRetryStrategy'); + const createSpy = vi.spyOn(client, 'createRetryStrategy'); const { retryStrategy } = client.getClientOptions()!; try { retryStrategy!({} as any); } catch { // No empty } - expect(createSpy.called).to.be.true; + expect(createSpy).toHaveBeenCalled(); }); }); describe('createRetryStrategy', () => { @@ -333,7 +337,7 @@ describe('ClientRedis', () => { it('should return undefined', () => { untypedClient.isManuallyClosed = true; const result = client.createRetryStrategy(0); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('when "retryAttempts" does not exist', () => { @@ -342,7 +346,7 @@ describe('ClientRedis', () => { untypedClient.options.options = {}; untypedClient.options.options.retryAttempts = undefined; const result = client.createRetryStrategy(1); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('when "attempts" count is max', () => { @@ -351,7 +355,7 @@ describe('ClientRedis', () => { untypedClient.options.options = {}; untypedClient.options.options.retryAttempts = 3; const result = client.createRetryStrategy(4); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('otherwise', () => { @@ -361,16 +365,16 @@ describe('ClientRedis', () => { untypedClient.options.retryAttempts = 3; untypedClient.options.retryDelay = 3; const result = client.createRetryStrategy(2); - expect(result).to.be.eql(untypedClient.options.retryDelay); + expect(result).toEqual(untypedClient.options.retryDelay); }); }); }); describe('dispatchEvent', () => { const msg = { pattern: 'pattern', data: 'data' }; - let publishStub: sinon.SinonStub, pubClient; + let publishStub: ReturnType, pubClient; beforeEach(() => { - publishStub = sinon.stub(); + publishStub = vi.fn(); pubClient = { publish: publishStub, }; @@ -378,15 +382,15 @@ describe('ClientRedis', () => { }); it('should publish packet', async () => { - publishStub.callsFake((a, b, c) => c()); + publishStub.mockImplementation((a, b, c) => c()); await client['dispatchEvent'](msg); - expect(publishStub.called).to.be.true; + expect(publishStub).toHaveBeenCalled(); }); it('should throw error', async () => { - publishStub.callsFake((a, b, c) => c(new Error())); + publishStub.mockImplementation((a, b, c) => c(new Error())); client['dispatchEvent'](msg).catch(err => - expect(err).to.be.instanceOf(Error), + expect(err).toBeInstanceOf(Error), ); }); }); diff --git a/packages/microservices/test/client/client-rmq.spec.ts b/packages/microservices/test/client/client-rmq.spec.ts index 243ef89051e..c6d5510dacf 100644 --- a/packages/microservices/test/client/client-rmq.spec.ts +++ b/packages/microservices/test/client/client-rmq.spec.ts @@ -1,43 +1,43 @@ -import { expect } from 'chai'; import { EventEmitter } from 'events'; import { EMPTY } from 'rxjs'; -import * as sinon from 'sinon'; -import { ClientRMQ } from '../../client/client-rmq'; -import { ReadPacket } from '../../interfaces'; -import { RmqRecord } from '../../record-builders'; +import { ClientRMQ } from '../../client/client-rmq.js'; +import { ReadPacket } from '../../interfaces/index.js'; +import { RmqRecord } from '../../record-builders/index.js'; describe('ClientRMQ', function () { - this.retries(10); - let client: ClientRMQ; let untypedClient: any; describe('connect', () => { - let createClientStub: sinon.SinonStub; - let registerErrorListenerSpy: sinon.SinonSpy; - let connect$Stub: sinon.SinonStub; + let createClientStub: ReturnType; + let registerErrorListenerSpy: ReturnType; + let connect$Stub: ReturnType; beforeEach(async () => { client = new ClientRMQ({}); untypedClient = client as any; - createClientStub = sinon.stub(client, 'createClient').callsFake(() => ({ - addListener: () => ({}), - removeListener: () => ({}), - })); - registerErrorListenerSpy = sinon.spy(client, 'registerErrorListener'); - connect$Stub = sinon.stub(client, 'connect$' as any).callsFake(() => ({ - subscribe: resolve => resolve(), - toPromise() { - return this; - }, - pipe() { - return this; - }, - })); - sinon - .stub(client, 'mergeDisconnectEvent') - .callsFake((_, source) => source); + createClientStub = vi + .spyOn(client, 'createClient') + .mockImplementation(() => ({ + addListener: () => ({}), + removeListener: () => ({}), + })); + registerErrorListenerSpy = vi.spyOn(client, 'registerErrorListener'); + connect$Stub = vi + .spyOn(client, 'connect$' as any) + .mockImplementation(() => ({ + subscribe: resolve => resolve(), + toPromise() { + return this; + }, + pipe() { + return this; + }, + })); + vi.spyOn(client, 'mergeDisconnectEvent').mockImplementation( + (_, source) => source, + ); }); describe('when is not connected', () => { beforeEach(async () => { @@ -49,13 +49,13 @@ describe('ClientRMQ', function () { } }); it('should call "registerErrorListener" once', async () => { - expect(registerErrorListenerSpy.called).to.be.true; + expect(registerErrorListenerSpy).toHaveBeenCalled(); }); it('should call "createClient" once', async () => { - expect(createClientStub.called).to.be.true; + expect(createClientStub).toHaveBeenCalled(); }); it('should call "connect$" once', async () => { - expect(connect$Stub.called).to.be.true; + expect(connect$Stub).toHaveBeenCalled(); }); }); describe('when is connected', () => { @@ -64,56 +64,58 @@ describe('ClientRMQ', function () { client['channel'] = { test: true }; }); it('should not call "createClient"', () => { - expect(createClientStub.called).to.be.false; + expect(createClientStub).not.toHaveBeenCalled(); }); it('should not call "registerErrorListener"', () => { - expect(registerErrorListenerSpy.called).to.be.false; + expect(registerErrorListenerSpy).not.toHaveBeenCalled(); }); it('should not call "connect$"', () => { - expect(connect$Stub.called).to.be.false; + expect(connect$Stub).not.toHaveBeenCalled(); }); }); }); describe('createChannel', () => { - let createChannelStub: sinon.SinonStub; - let setupChannelStub: sinon.SinonStub; + let createChannelStub: ReturnType; + let setupChannelStub: ReturnType; beforeEach(() => { - setupChannelStub = sinon - .stub(client, 'setupChannel') - .callsFake((_, done) => done()); - createChannelStub = sinon.stub().callsFake(({ setup }) => setup()); + setupChannelStub = vi + .spyOn(client, 'setupChannel') + .mockImplementation((_, done) => done()); + createChannelStub = vi.fn().mockImplementation(({ setup }) => setup()); client['client'] = { createChannel: createChannelStub }; }); afterEach(() => { - setupChannelStub.restore(); + setupChannelStub.mockRestore(); }); it('should call "createChannel" method of the client instance', async () => { await client.createChannel(); - expect(createChannelStub.called).to.be.true; + expect(createChannelStub).toHaveBeenCalled(); }); it('should call "setupChannel" method of the client instance', async () => { await client.createChannel(); - expect(setupChannelStub.called).to.be.true; + expect(setupChannelStub).toHaveBeenCalled(); }); }); describe('consumeChannel', () => { - let consumeStub: sinon.SinonStub; + let consumeStub: ReturnType; const channel: any = {}; beforeEach(() => { client['responseEmitter'] = new EventEmitter(); - consumeStub = sinon - .stub() - .callsFake((_, done) => done({ properties: { correlationId: 1 } })); + consumeStub = vi + .fn() + .mockImplementation((_, done) => + done({ properties: { correlationId: 1 } }), + ); channel.consume = consumeStub; }); it('should call "consume" method of the channel instance', async () => { await client.consumeChannel(channel); - expect(consumeStub.called).to.be.true; + expect(consumeStub).toHaveBeenCalled(); }); }); @@ -124,7 +126,7 @@ describe('ClientRMQ', function () { const isGlobalPrefetchCount = true; const prefetchCount = 10; - let consumeStub: sinon.SinonStub; + let consumeStub: ReturnType; let channel: any = {}; beforeEach(() => { @@ -133,63 +135,67 @@ describe('ClientRMQ', function () { untypedClient['options'] = { isGlobalPrefetchCount, prefetchCount }; channel = { - assertQueue: sinon.spy(() => ({})), - prefetch: sinon.spy(), - bindQueue: sinon.spy(), - assertExchange: sinon.spy(), + assertQueue: vi.fn(), + prefetch: vi.fn(), + bindQueue: vi.fn(), + assertExchange: vi.fn(), }; - consumeStub = sinon.stub(client, 'consumeChannel').callsFake(() => null!); + consumeStub = vi + .spyOn(client, 'consumeChannel') + .mockImplementation(() => null!); }); afterEach(() => { - consumeStub.restore(); + consumeStub.mockRestore(); }); it('should call "assertQueue" with queue and queue options when noAssert is false', async () => { client['noAssert'] = false; await client.setupChannel(channel, () => null); - expect(channel.assertQueue.calledWith(queue, queueOptions)).to.be.true; + expect(channel.assertQueue).toHaveBeenCalledWith(queue, queueOptions); }); it('should not call "assertQueue" when noAssert is true', async () => { client['noAssert'] = true; await client.setupChannel(channel, () => null); - expect(channel.assertQueue.called).not.to.be.true; + expect(channel.assertQueue).not.toHaveBeenCalled(); }); it('should not call "assertQueue" when exchangeType is fanout', async () => { untypedClient['options']['exchangeType'] = 'fanout'; untypedClient['options']['exchange'] = exchange; await client.setupChannel(channel, () => null); - expect(channel.assertQueue.called).not.to.be.true; + expect(channel.assertQueue).not.toHaveBeenCalled(); }); it('should not call "assertQueue" when wildcards is true', async () => { untypedClient['options']['wildcards'] = true; await client.setupChannel(channel, () => null); - expect(channel.assertQueue.called).not.to.be.true; + expect(channel.assertQueue).not.toHaveBeenCalled(); }); it('should not call "bindQueue" when exchangeType is fanout', async () => { untypedClient['options']['exchangeType'] = 'fanout'; untypedClient['options']['exchange'] = exchange; await client.setupChannel(channel, () => null); - expect(channel.bindQueue.called).not.to.be.true; + expect(channel.bindQueue).not.toHaveBeenCalled(); }); it('should not call "bindQueue" when wildcards is true', async () => { untypedClient['options']['wildcards'] = true; await client.setupChannel(channel, () => null); - expect(channel.bindQueue.called).not.to.be.true; + expect(channel.bindQueue).not.toHaveBeenCalled(); }); it('should call "prefetch" with prefetchCount and "isGlobalPrefetchCount"', async () => { await client.setupChannel(channel, () => null); - expect(channel.prefetch.calledWith(prefetchCount, isGlobalPrefetchCount)) - .to.be.true; + expect(channel.prefetch).toHaveBeenCalledWith( + prefetchCount, + isGlobalPrefetchCount, + ); }); it('should call "consumeChannel" method', async () => { await client.setupChannel(channel, () => null); - expect(consumeStub.called).to.be.true; + expect(consumeStub).toHaveBeenCalled(); }); it('should call "resolve" function', async () => { - const resolve = sinon.spy(); + const resolve = vi.fn(); await client.setupChannel(channel, resolve); - expect(resolve.called).to.be.true; + expect(resolve).toHaveBeenCalled(); }); }); @@ -202,7 +208,7 @@ describe('ClientRMQ', function () { }; client .mergeDisconnectEvent(instance, EMPTY) - .subscribe({ error: (err: any) => expect(err).to.be.eql(error) }); + .subscribe({ error: (err: any) => expect(err).toEqual(error) }); }); }); @@ -210,20 +216,20 @@ describe('ClientRMQ', function () { const pattern = 'test'; const exchange = 'test.exchange'; let msg: ReadPacket; - let connectSpy: sinon.SinonSpy, - sendToQueueStub: sinon.SinonStub, - publishStub: sinon.SinonStub, - eventSpy: sinon.SinonSpy; + let connectSpy: ReturnType, + sendToQueueStub: ReturnType, + publishStub: ReturnType, + eventSpy: ReturnType; beforeEach(() => { client = new ClientRMQ({}); untypedClient = client as any; msg = { pattern, data: 'data' }; - connectSpy = sinon.spy(client, 'connect'); - eventSpy = sinon.spy(); - sendToQueueStub = sinon.stub().callsFake(() => ({ catch: sinon.spy() })); - publishStub = sinon.stub().callsFake(() => ({ catch: sinon.spy() })); + connectSpy = vi.spyOn(client, 'connect'); + eventSpy = vi.fn(); + sendToQueueStub = vi.fn().mockImplementation(() => ({ catch: vi.fn() })); + publishStub = vi.fn().mockImplementation(() => ({ catch: vi.fn() })); client['channel'] = { sendToQueue: sendToQueueStub, @@ -234,55 +240,55 @@ describe('ClientRMQ', function () { }); afterEach(() => { - connectSpy.restore(); + connectSpy.mockRestore(); }); it('should send message to a proper queue', () => { client['publish'](msg, () => { - expect(sendToQueueStub.called).to.be.true; - expect(sendToQueueStub.getCall(0).args[0]).to.be.eql(client['queue']); + expect(sendToQueueStub).toHaveBeenCalled(); + expect(sendToQueueStub.mock.calls[0][0]).toEqual(client['queue']); }); }); it('should send message to exchange when exchangeType is fanout', async () => { untypedClient['options']['exchangeType'] = 'fanout'; untypedClient['options']['exchange'] = exchange; client['publish'](msg, () => { - expect(publishStub.called).to.be.true; - expect(publishStub.getCall(0).args[0]).to.be.eql(exchange); + expect(publishStub).toHaveBeenCalled(); + expect(publishStub.mock.calls[0][0]).toEqual(exchange); }); }); it('should send buffer from stringified message', () => { client['publish'](msg, () => { - expect(sendToQueueStub.called).to.be.true; - expect(sendToQueueStub.getCall(1).args[1]).to.be.eql( + expect(sendToQueueStub).toHaveBeenCalled(); + expect(sendToQueueStub.mock.calls[1][1]).toEqual( Buffer.from(JSON.stringify(msg)), ); }); }); describe('dispose callback', () => { - let unsubscribeSpy: sinon.SinonSpy, subscription; + let unsubscribeSpy: ReturnType, subscription; beforeEach(async () => { - unsubscribeSpy = sinon.spy(); + unsubscribeSpy = vi.fn(); client['responseEmitter'] = { removeListener: unsubscribeSpy, - on: sinon.spy(), + on: vi.fn(), } as any as EventEmitter; - subscription = client['publish'](msg, sinon.spy()); + subscription = client['publish'](msg, vi.fn()); subscription(); }); it('should unsubscribe', () => { - expect(unsubscribeSpy.called).to.be.true; + expect(unsubscribeSpy).toHaveBeenCalled(); }); }); describe('headers', () => { it('should not generate headers if none are configured', () => { client['publish'](msg, () => { - expect(sendToQueueStub.getCall(0).args[2].headers).to.be.undefined; + expect(sendToQueueStub.mock.calls[0][2].headers).toBeUndefined(); }); }); @@ -291,7 +297,7 @@ describe('ClientRMQ', function () { msg.data = new RmqRecord('data', { headers: requestHeaders }); client['publish'](msg, () => { - expect(sendToQueueStub.getCall(0).args[2].headers).to.eql( + expect(sendToQueueStub.mock.calls[0][2].headers).toEqual( requestHeaders, ); }); @@ -305,7 +311,7 @@ describe('ClientRMQ', function () { msg.data = new RmqRecord('data', { headers: requestHeaders }); client['publish'](msg, () => { - expect(sendToQueueStub.getCall(0).args[2].headers).to.eql({ + expect(sendToQueueStub.mock.calls[0][2].headers).toEqual({ ...staticHeaders, ...requestHeaders, }); @@ -320,7 +326,7 @@ describe('ClientRMQ', function () { msg.data = new RmqRecord('data', { headers: requestHeaders }); client['publish'](msg, () => { - expect(sendToQueueStub.getCall(0).args[2].headers).to.eql( + expect(sendToQueueStub.mock.calls[0][2].headers).toEqual( requestHeaders, ); }); @@ -330,10 +336,10 @@ describe('ClientRMQ', function () { describe('handleMessage', () => { describe('when error', () => { - let callback: sinon.SinonSpy; + let callback: ReturnType; beforeEach(() => { - callback = sinon.spy(); + callback = vi.fn(); }); it('should call callback with correct object', async () => { const packet = { @@ -342,20 +348,18 @@ describe('ClientRMQ', function () { isDisposed: false, }; await client.handleMessage(packet, callback); - expect( - callback.calledWith({ - err: packet.err, - response: 'test', - isDisposed: true, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: packet.err, + response: 'test', + isDisposed: true, + }); }); }); describe('when disposed', () => { - let callback: sinon.SinonSpy; + let callback: ReturnType; beforeEach(() => { - callback = sinon.spy(); + callback = vi.fn(); }); it('should call callback with correct object', async () => { const packet = { @@ -363,21 +367,19 @@ describe('ClientRMQ', function () { isDisposed: true, }; await client.handleMessage(packet, callback); - expect( - callback.calledWith({ - err: undefined, - response: 'test', - isDisposed: true, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: undefined, + response: 'test', + isDisposed: true, + }); }); }); describe('when response', () => { - let callback: sinon.SinonSpy; + let callback: ReturnType; beforeEach(() => { - callback = sinon.spy(); + callback = vi.fn(); }); it('should call callback with correct object', async () => { const packet = { @@ -385,48 +387,48 @@ describe('ClientRMQ', function () { isDisposed: false, }; await client.handleMessage(packet, callback); - expect( - callback.calledWith({ - err: undefined, - response: packet.response, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: undefined, + response: packet.response, + }); }); }); }); describe('close', () => { - let channelCloseSpy: sinon.SinonSpy; - let clientCloseSpy: sinon.SinonSpy; + let channelCloseSpy: ReturnType; + let clientCloseSpy: ReturnType; beforeEach(() => { - channelCloseSpy = sinon.spy(); - clientCloseSpy = sinon.spy(); + channelCloseSpy = vi.fn(); + clientCloseSpy = vi.fn(); untypedClient.channel = { close: channelCloseSpy }; untypedClient.client = { close: clientCloseSpy }; }); it('should close channel when it is not null', async () => { await client.close(); - expect(channelCloseSpy.called).to.be.true; + expect(channelCloseSpy).toHaveBeenCalled(); }); it('should close client when it is not null', async () => { await client.close(); - expect(clientCloseSpy.called).to.be.true; + expect(clientCloseSpy).toHaveBeenCalled(); }); }); describe('dispatchEvent', () => { let msg: ReadPacket; const exchange = 'test.exchange'; - let sendToQueueStub: sinon.SinonStub, publishStub: sinon.SinonStub, channel; + let sendToQueueStub: ReturnType, + publishStub: ReturnType, + channel; beforeEach(() => { client = new ClientRMQ({}); untypedClient = client as any; msg = { pattern: 'pattern', data: 'data' }; - sendToQueueStub = sinon.stub(); - publishStub = sinon.stub(); + sendToQueueStub = vi.fn(); + publishStub = vi.fn(); channel = { sendToQueue: sendToQueueStub, publish: publishStub, @@ -435,47 +437,47 @@ describe('ClientRMQ', function () { }); it('should publish packet', async () => { - sendToQueueStub.callsFake((a, b, c, d) => d()); + sendToQueueStub.mockImplementation((a, b, c, d) => d()); await client['dispatchEvent'](msg); - expect(sendToQueueStub.called).to.be.true; + expect(sendToQueueStub).toHaveBeenCalled(); }); it('should publish packet to exchange when exchangeType is fanout', async () => { untypedClient['options']['exchangeType'] = 'fanout'; untypedClient['options']['exchange'] = exchange; - publishStub.callsFake((a, b, c, d, f) => f()); + publishStub.mockImplementation((a, b, c, d, f) => f()); await client['dispatchEvent'](msg); - expect(publishStub.called).to.be.true; - expect(publishStub.getCall(0).args[0]).to.be.eql(exchange); + expect(publishStub).toHaveBeenCalled(); + expect(publishStub.mock.calls[0][0]).toEqual(exchange); }); it('should throw error', async () => { - sendToQueueStub.callsFake((a, b, c, d) => d(new Error())); + sendToQueueStub.mockImplementation((a, b, c, d) => d(new Error())); client['dispatchEvent'](msg).catch(err => - expect(err).to.be.instanceOf(Error), + expect(err).toBeInstanceOf(Error), ); }); describe('headers', () => { it('should not generate headers if none are configured', async () => { - sendToQueueStub.callsFake((a, b, c, d) => d()); + sendToQueueStub.mockImplementation((a, b, c, d) => d()); await client['dispatchEvent'](msg); - expect(sendToQueueStub.getCall(0).args[2].headers).to.be.undefined; + expect(sendToQueueStub.mock.calls[0][2].headers).toBeUndefined(); }); it('should send packet headers', async () => { - sendToQueueStub.callsFake((a, b, c, d) => d()); + sendToQueueStub.mockImplementation((a, b, c, d) => d()); const requestHeaders = { '1': '123' }; msg.data = new RmqRecord('data', { headers: requestHeaders }); await client['dispatchEvent'](msg); - expect(sendToQueueStub.getCall(0).args[2].headers).to.eql( + expect(sendToQueueStub.mock.calls[0][2].headers).toEqual( requestHeaders, ); }); it('should combine packet and static headers', async () => { - sendToQueueStub.callsFake((a, b, c, d) => d()); + sendToQueueStub.mockImplementation((a, b, c, d) => d()); const staticHeaders = { 'client-id': 'some-client-id' }; untypedClient.options.headers = staticHeaders; @@ -483,14 +485,14 @@ describe('ClientRMQ', function () { msg.data = new RmqRecord('data', { headers: requestHeaders }); await client['dispatchEvent'](msg); - expect(sendToQueueStub.getCall(0).args[2].headers).to.eql({ + expect(sendToQueueStub.mock.calls[0][2].headers).toEqual({ ...staticHeaders, ...requestHeaders, }); }); it('should prefer packet headers over static headers', async () => { - sendToQueueStub.callsFake((a, b, c, d) => d()); + sendToQueueStub.mockImplementation((a, b, c, d) => d()); const staticHeaders = { 'client-id': 'some-client-id' }; untypedClient.options.headers = staticHeaders; @@ -498,7 +500,7 @@ describe('ClientRMQ', function () { msg.data = new RmqRecord('data', { headers: requestHeaders }); await client['dispatchEvent'](msg); - expect(sendToQueueStub.getCall(0).args[2].headers).to.eql( + expect(sendToQueueStub.mock.calls[0][2].headers).toEqual( requestHeaders, ); }); diff --git a/packages/microservices/test/client/client-tcp.spec.ts b/packages/microservices/test/client/client-tcp.spec.ts index dbf3d85b892..22bf3777015 100644 --- a/packages/microservices/test/client/client-tcp.spec.ts +++ b/packages/microservices/test/client/client-tcp.spec.ts @@ -1,16 +1,13 @@ -import { expect } from 'chai'; import { Socket as NetSocket } from 'net'; -import * as sinon from 'sinon'; import { TLSSocket } from 'tls'; -import { ClientTCP } from '../../client/client-tcp'; -import { TcpEventsMap } from '../../events/tcp.events'; -import { TcpSocket } from '../../helpers/tcp-socket'; +import { ClientTCP } from '../../client/client-tcp.js'; +import { TcpSocket } from '../../helpers/tcp-socket.js'; describe('ClientTCP', () => { let client: ClientTCP; let untypedClient: any; let socket: any; - let createSocketStub: sinon.SinonStub; + let createSocketStub: ReturnType; beforeEach(() => { client = new ClientTCP({}); @@ -20,22 +17,22 @@ describe('ClientTCP', () => { event !== 'error' && event !== 'close' && callback({}); socket = { - connect: sinon.stub(), - on: sinon.stub().callsFake(onFakeCallback), + connect: vi.fn(), + on: vi.fn().mockImplementation(onFakeCallback), netSocket: { - addListener: sinon.stub().callsFake(onFakeCallback), - removeListener: sinon.spy(), - once: sinon.stub().callsFake(onFakeCallback), + addListener: vi.fn().mockImplementation(onFakeCallback), + removeListener: vi.fn(), + once: vi.fn().mockImplementation(onFakeCallback), }, - sendMessage: sinon.spy(), - end: sinon.spy(), + sendMessage: vi.fn(), + end: vi.fn(), }; - createSocketStub = sinon - .stub(client, 'createSocket') - .callsFake(() => socket); + createSocketStub = vi + .spyOn(client, 'createSocket') + .mockImplementation(() => socket); }); afterEach(() => { - createSocketStub.restore(); + createSocketStub.mockRestore(); }); describe('publish', () => { let msg; @@ -51,79 +48,73 @@ describe('ClientTCP', () => { it('should remove listener from routing map', () => { client['publish'](msg, () => ({}))(); - expect(client['routingMap'].size).to.be.eq(0); + expect(client['routingMap'].size).toBe(0); }); }); describe('on error', () => { it('should call callback', () => { - const callback = sinon.spy(); - sinon.stub(client, 'assignPacketId' as any).callsFake(() => { + const callback = vi.fn(); + vi.spyOn(client, 'assignPacketId' as any).mockImplementation(() => { throw new Error(); }); client['publish'](msg, callback); - expect(callback.called).to.be.true; - expect(callback.getCall(0).args[0].err).to.be.instanceof(Error); + expect(callback).toHaveBeenCalled(); + expect(callback.mock.calls[0][0].err).toBeInstanceOf(Error); }); }); }); describe('handleResponse', () => { - let callback: sinon.SinonSpy; + let callback: ReturnType; const id = '1'; describe('when disposed', () => { beforeEach(async () => { - callback = sinon.spy(); + callback = vi.fn(); client['routingMap'].set(id, callback); await client.handleResponse({ id, isDisposed: true }); }); it('should emit disposed callback', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - err: undefined, - response: undefined, - isDisposed: true, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: undefined, + response: undefined, + isDisposed: true, + }); }); }); describe('when not disposed', () => { let buffer; beforeEach(async () => { buffer = { id, err: undefined, response: 'res' }; - callback = sinon.spy(); + callback = vi.fn(); client['routingMap'].set(id, callback); await client.handleResponse(buffer); }); it('should not end server', () => { - expect(socket.end.called).to.be.false; + expect(socket.end).not.toHaveBeenCalled(); }); it('should call callback with error and response data', () => { - expect(callback.called).to.be.true; - expect( - callback.calledWith({ - err: buffer.err, - response: buffer.response, - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: buffer.err, + response: buffer.response, + }); }); }); }); describe('connect', () => { - let registerConnectListenerSpy: sinon.SinonSpy; - let registerErrorListenerSpy: sinon.SinonSpy; - let registerCloseListenerSpy: sinon.SinonSpy; - let connect$Stub: sinon.SinonStub; + let registerConnectListenerSpy: ReturnType; + let registerErrorListenerSpy: ReturnType; + let registerCloseListenerSpy: ReturnType; + let connect$Stub: ReturnType; beforeEach(async () => { - registerConnectListenerSpy = sinon.spy(client, 'registerConnectListener'); - registerErrorListenerSpy = sinon.spy(client, 'registerErrorListener'); - registerCloseListenerSpy = sinon.spy(client, 'registerCloseListener'); + registerConnectListenerSpy = vi.spyOn(client, 'registerConnectListener'); + registerErrorListenerSpy = vi.spyOn(client, 'registerErrorListener'); + registerCloseListenerSpy = vi.spyOn(client, 'registerCloseListener'); }); afterEach(() => { - registerConnectListenerSpy.restore(); - registerErrorListenerSpy.restore(); - registerCloseListenerSpy.restore; + registerConnectListenerSpy.mockRestore(); + registerErrorListenerSpy.mockRestore(); + registerCloseListenerSpy.mockRestore(); }); describe('when is not connected', () => { beforeEach(async () => { @@ -132,31 +123,31 @@ describe('ClientTCP', () => { subscribe: ({ complete }) => complete(), pipe: () => source, }; - connect$Stub = sinon - .stub(client, 'connect$' as any) - .callsFake(() => source); + connect$Stub = vi + .spyOn(client, 'connect$' as any) + .mockImplementation(() => source); await client.connect(); }); afterEach(() => { - connect$Stub.restore(); + connect$Stub.mockRestore(); }); it('should call "registerConnectListener" once', async () => { - expect(registerConnectListenerSpy.called).to.be.true; + expect(registerConnectListenerSpy).toHaveBeenCalled(); }); it('should call "registerErrorListener" once', async () => { - expect(registerErrorListenerSpy.called).to.be.true; + expect(registerErrorListenerSpy).toHaveBeenCalled(); }); it('should call "registerCloseListener" once', async () => { - expect(registerCloseListenerSpy.called).to.be.true; + expect(registerCloseListenerSpy).toHaveBeenCalled(); }); it('should call "createSocket" once', async () => { - expect(createSocketStub.called).to.be.true; + expect(createSocketStub).toHaveBeenCalled(); }); it('should call "connect$" once', async () => { - expect(connect$Stub.called).to.be.true; + expect(connect$Stub).toHaveBeenCalled(); }); it('should listen on messages', () => { - expect(socket.on.called).to.be.true; + expect(socket.on).toHaveBeenCalled(); }); }); describe('when is connected', () => { @@ -164,20 +155,20 @@ describe('ClientTCP', () => { client['isConnected'] = true; }); it('should not call "createSocket"', () => { - expect(createSocketStub.called).to.be.false; + expect(createSocketStub).not.toHaveBeenCalled(); }); it('should not call "bindEvents"', () => { - expect(registerConnectListenerSpy.called).to.be.false; + expect(registerConnectListenerSpy).not.toHaveBeenCalled(); }); }); }); describe('close', () => { let routingMap: Map; - let callback: sinon.SinonSpy; + let callback: ReturnType; beforeEach(() => { routingMap = new Map(); - callback = sinon.spy(); + callback = vi.fn(); routingMap.set('some id', callback); untypedClient.socket = socket; @@ -185,58 +176,62 @@ describe('ClientTCP', () => { client.close(); }); it('should end() socket', () => { - expect(socket.end.called).to.be.true; + expect(socket.end).toHaveBeenCalled(); }); it('should set "socket" to null', () => { - expect(untypedClient.socket).to.be.null; + expect(untypedClient.socket).toBeNull(); }); it('should clear out the routing map', () => { - expect(untypedClient.routingMap.size).to.be.eq(0); + expect(untypedClient.routingMap.size).toBe(0); }); it('should call callbacks', () => { - expect( - callback.calledWith({ - err: sinon.match({ message: 'Connection closed' }), - }), - ).to.be.true; + expect(callback).toHaveBeenCalledWith({ + err: expect.objectContaining({ message: 'Connection closed' }), + }); }); }); describe('registerErrorListener', () => { it('should bind error event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerErrorListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(TcpEventsMap.ERROR); + expect(callback.mock.calls[0][0]).toEqual('error'); }); }); describe('registerCloseListener', () => { it('should bind close event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerCloseListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(TcpEventsMap.CLOSE); + expect(callback.mock.calls[0][0]).toEqual('close'); }); }); describe('registerConnectListener', () => { it('should bind connect event handler', () => { - const callback = sinon.stub().callsFake((_, fn) => fn({ code: 'test' })); + const callback = vi + .fn() + .mockImplementation((_, fn) => fn({ code: 'test' })); const emitter = { on: callback, }; client.registerConnectListener(emitter as any); - expect(callback.getCall(0).args[0]).to.be.eql(TcpEventsMap.CONNECT); + expect(callback.mock.calls[0][0]).toEqual('connect'); }); }); describe('dispatchEvent', () => { const msg = { pattern: 'pattern', data: 'data' }; - let sendMessageStub: sinon.SinonStub, internalSocket; + let sendMessageStub: ReturnType, internalSocket; beforeEach(() => { - sendMessageStub = sinon.stub(); + sendMessageStub = vi.fn(); internalSocket = { sendMessage: sendMessageStub, }; @@ -246,7 +241,7 @@ describe('ClientTCP', () => { it('should publish packet', async () => { await client['dispatchEvent'](msg); - expect(sendMessageStub.called).to.be.true; + expect(sendMessageStub).toHaveBeenCalled(); }); }); @@ -254,11 +249,11 @@ describe('ClientTCP', () => { it('should upgrade to TLS', () => { const client = new ClientTCP({ tlsOptions: {} }); const jsonSocket = client.createSocket(); - expect(jsonSocket.socket).instanceOf(TLSSocket); + expect(jsonSocket.socket).toBeInstanceOf(TLSSocket); }); it('should not upgrade to TLS, if not requested', () => { const jsonSocket = new ClientTCP({}).createSocket(); - expect(jsonSocket.socket).instanceOf(NetSocket); + expect(jsonSocket.socket).toBeInstanceOf(NetSocket); }); }); @@ -269,7 +264,7 @@ describe('ClientTCP', () => { it('should use default maxBufferSize', () => { const client = new ClientTCP({}); const socket = client.createSocket(); - expect(socket['maxBufferSize']).to.equal(DEFAULT_MAX_BUFFER_SIZE); + expect(socket['maxBufferSize']).toBe(DEFAULT_MAX_BUFFER_SIZE); }); }); @@ -278,14 +273,14 @@ describe('ClientTCP', () => { const customSize = 5000; const client = new ClientTCP({ maxBufferSize: customSize }); const socket = client.createSocket(); - expect(socket['maxBufferSize']).to.equal(customSize); + expect(socket['maxBufferSize']).toBe(customSize); }); it('should pass maxBufferSize to JsonSocket', () => { const customSize = 10000; const client = new ClientTCP({ maxBufferSize: customSize }); const socket = client.createSocket(); - expect(socket['maxBufferSize']).to.equal(customSize); + expect(socket['maxBufferSize']).toBe(customSize); }); }); @@ -304,9 +299,9 @@ describe('ClientTCP', () => { maxBufferSize: 5000, }); const socket = client.createSocket(); - expect(socket).to.be.instanceOf(CustomSocket); + expect(socket).toBeInstanceOf(CustomSocket); // Custom socket should not have maxBufferSize property - expect(socket['maxBufferSize']).to.be.undefined; + expect(socket['maxBufferSize']).toBeUndefined(); }); }); }); diff --git a/packages/microservices/test/container.spec.ts b/packages/microservices/test/container.spec.ts index 402d8813258..a8b5496a5db 100644 --- a/packages/microservices/test/container.spec.ts +++ b/packages/microservices/test/container.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { ClientsContainer } from '../container'; +import { ClientsContainer } from '../container.js'; describe('ClientsContainer', () => { let instance: ClientsContainer; @@ -10,14 +9,14 @@ describe('ClientsContainer', () => { it('should return array of clients', () => { const clients = [1, 2, 3]; (instance as any).clients = clients; - expect(instance.getAllClients()).to.be.eql(clients); + expect(instance.getAllClients()).toEqual(clients); }); }); describe('addClient', () => { it('should push client into clients array', () => { const client = 'test'; instance.addClient(client as any); - expect(instance.getAllClients()).to.be.deep.equal([client]); + expect(instance.getAllClients()).toEqual([client]); }); }); describe('clear', () => { @@ -25,7 +24,7 @@ describe('ClientsContainer', () => { const clients = [1, 2, 3]; (instance as any).clients = clients; instance.clear(); - expect(instance.getAllClients()).to.be.deep.equal([]); + expect(instance.getAllClients()).toEqual([]); }); }); }); diff --git a/packages/microservices/test/context/exception-filters-context.spec.ts b/packages/microservices/test/context/exception-filters-context.spec.ts index f55c4019e2d..a7ca64e5214 100644 --- a/packages/microservices/test/context/exception-filters-context.spec.ts +++ b/packages/microservices/test/context/exception-filters-context.spec.ts @@ -1,11 +1,9 @@ -import { NestContainer } from '@nestjs/core/injector/container'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Catch } from '../../../common/decorators/core/catch.decorator'; -import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator'; -import { ApplicationConfig } from '../../../core/application-config'; -import { InstanceWrapper } from '../../../core/injector/instance-wrapper'; -import { ExceptionFiltersContext } from '../../context/exception-filters-context'; +import { NestContainer } from '@nestjs/core/injector/container.js'; +import { Catch } from '../../../common/decorators/core/catch.decorator.js'; +import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator.js'; +import { ApplicationConfig } from '../../../core/application-config.js'; +import { InstanceWrapper } from '../../../core/injector/instance-wrapper.js'; +import { ExceptionFiltersContext } from '../../context/exception-filters-context.js'; describe('ExceptionFiltersContext', () => { let applicationConfig: ApplicationConfig; @@ -28,7 +26,7 @@ describe('ExceptionFiltersContext', () => { describe('when filters metadata is empty', () => { class EmptyMetadata {} beforeEach(() => { - sinon.stub(exceptionFilter, 'createContext').returns([]); + vi.spyOn(exceptionFilter, 'createContext').mockReturnValue([]); }); it('should return plain ExceptionHandler object', () => { const filter = exceptionFilter.create( @@ -36,7 +34,7 @@ describe('ExceptionFiltersContext', () => { () => ({}) as any, undefined!, ); - expect((filter as any).filters).to.be.empty; + expect((filter as any).filters).toHaveLength(0); }); }); describe('when filters metadata is not empty', () => { @@ -49,7 +47,7 @@ describe('ExceptionFiltersContext', () => { () => ({}) as any, undefined!, ); - expect((filter as any).filters).to.not.be.empty; + expect((filter as any).filters).not.toHaveLength(0); }); }); }); @@ -58,7 +56,7 @@ describe('ExceptionFiltersContext', () => { describe('when contextId is static and inquirerId is nil', () => { it('should return global filters', () => { const expectedResult = applicationConfig.getGlobalFilters(); - expect(exceptionFilter.getGlobalMetadata()).to.be.equal(expectedResult); + expect(exceptionFilter.getGlobalMetadata()).toBe(expectedResult); }); }); describe('otherwise', () => { @@ -68,19 +66,20 @@ describe('ExceptionFiltersContext', () => { const instance = 'request-scoped'; const scopedFilterWrappers = [instanceWrapper]; - sinon - .stub(applicationConfig, 'getGlobalFilters') - .callsFake(() => globalFilters); - sinon - .stub(applicationConfig, 'getGlobalRequestFilters') - .callsFake(() => scopedFilterWrappers); - sinon - .stub(instanceWrapper, 'getInstanceByContextId') - .callsFake(() => ({ instance }) as any); + vi.spyOn(applicationConfig, 'getGlobalFilters').mockImplementation( + () => globalFilters, + ); + vi.spyOn( + applicationConfig, + 'getGlobalRequestFilters', + ).mockImplementation(() => scopedFilterWrappers); + vi.spyOn(instanceWrapper, 'getInstanceByContextId').mockImplementation( + () => ({ instance }) as any, + ); - expect(exceptionFilter.getGlobalMetadata({ id: 3 })).to.contains( - instance, - ...globalFilters, + const result = exceptionFilter.getGlobalMetadata({ id: 3 }); + expect(result).toEqual( + expect.arrayContaining([instance, ...globalFilters]), ); }); }); diff --git a/packages/microservices/test/context/request-context-host.spec.ts b/packages/microservices/test/context/request-context-host.spec.ts index 7c7fb09268b..3ed0f1ffef5 100644 --- a/packages/microservices/test/context/request-context-host.spec.ts +++ b/packages/microservices/test/context/request-context-host.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { RequestContextHost } from '../../context/request-context-host'; -import { BaseRpcContext } from '../../ctx-host/base-rpc.context'; +import { RequestContextHost } from '../../context/request-context-host.js'; +import { BaseRpcContext } from '../../ctx-host/base-rpc.context.js'; describe('RequestContextHost', () => { const data = { test: true }; @@ -13,17 +12,17 @@ describe('RequestContextHost', () => { }); describe('getData', () => { it('should return "data" property', () => { - expect(ctxHost.getData()).to.be.eql(data); + expect(ctxHost.getData()).toEqual(data); }); }); describe('getContext', () => { it('should return "context" property', () => { - expect(ctxHost.getContext()).to.be.eql(ctx); + expect(ctxHost.getContext()).toEqual(ctx); }); }); describe('getPattern', () => { it('should return "pattern" property', () => { - expect(ctxHost.getPattern()).to.be.eql(pattern); + expect(ctxHost.getPattern()).toEqual(pattern); }); }); }); diff --git a/packages/microservices/test/context/rpc-context-creator.spec.ts b/packages/microservices/test/context/rpc-context-creator.spec.ts index 8b9f11d23d9..77299ed2445 100644 --- a/packages/microservices/test/context/rpc-context-creator.spec.ts +++ b/packages/microservices/test/context/rpc-context-creator.spec.ts @@ -1,7 +1,5 @@ -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; -import { of } from 'rxjs'; -import * as sinon from 'sinon'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; +import { Observable, of } from 'rxjs'; import { Injectable, UseGuards, UsePipes } from '../../../common'; import { CUSTOM_ROUTE_ARGS_METADATA } from '../../../common/constants'; import { ApplicationConfig } from '../../../core/application-config'; @@ -58,7 +56,7 @@ describe('RpcContextCreator', () => { container, new ApplicationConfig() as any, ); - sinon.stub(rpcProxy, 'create').callsFake(a => a); + vi.spyOn(rpcProxy, 'create').mockImplementation(a => a); pipesCreator = new PipesContextCreator(container); pipesConsumer = new PipesConsumer(); @@ -78,32 +76,51 @@ describe('RpcContextCreator', () => { instance = new Test(); module = 'test'; }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + describe('create', () => { it('should create exception handler', () => { - const handlerCreateSpy = sinon.spy(exceptionFiltersContext, 'create'); + const handlerCreateSpy = vi.spyOn(exceptionFiltersContext, 'create'); contextCreator.create(instance, instance.test, module, 'test'); - expect( - handlerCreateSpy.calledWith(instance, instance.test as any, module), - ).to.be.true; + expect(handlerCreateSpy).toHaveBeenCalledWith( + instance, + instance.test as any, + module, + expect.anything(), + undefined, + ); }); it('should create pipes context', () => { - const pipesCreateSpy = sinon.spy(pipesCreator, 'create'); + const pipesCreateSpy = vi.spyOn(pipesCreator, 'create'); contextCreator.create(instance, instance.test, module, 'test'); - expect(pipesCreateSpy.calledWith(instance, instance.test as any, module)) - .to.be.true; + expect(pipesCreateSpy).toHaveBeenCalledWith( + instance, + instance.test as any, + module, + expect.anything(), + undefined, + ); }); it('should create guards context', () => { - const guardsCreateSpy = sinon.spy(guardsContextCreator, 'create'); + const guardsCreateSpy = vi.spyOn(guardsContextCreator, 'create'); contextCreator.create(instance, instance.test, module, 'test'); - expect(guardsCreateSpy.calledWith(instance, instance.test, module)).to.be - .true; + expect(guardsCreateSpy).toHaveBeenCalledWith( + instance, + instance.test, + module, + expect.anything(), + undefined, + ); }); describe('when proxy called', () => { it('should call guards consumer `tryActivate`', async () => { - const tryActivateSpy = sinon.spy(guardsConsumer, 'tryActivate'); - sinon - .stub(guardsContextCreator, 'create') - .callsFake(() => [{ canActivate: () => true }]); + const tryActivateSpy = vi.spyOn(guardsConsumer, 'tryActivate'); + vi.spyOn(guardsContextCreator, 'create').mockImplementation( + () => [{ canActivate: () => true }] as any, + ); const proxy = contextCreator.create( instance, instance.test, @@ -113,13 +130,13 @@ describe('RpcContextCreator', () => { const data = 'test'; await proxy(data); - expect(tryActivateSpy.called).to.be.true; + expect(tryActivateSpy).toHaveBeenCalled(); }); describe('when can not activate', () => { it('should throw forbidden exception', async () => { - sinon - .stub(guardsConsumer, 'tryActivate') - .callsFake(async () => false); + vi.spyOn(guardsConsumer, 'tryActivate').mockImplementation( + async () => false, + ); const proxy = contextCreator.create( instance, @@ -130,7 +147,7 @@ describe('RpcContextCreator', () => { const data = 'test'; proxy(null, data).catch(err => - expect(err).to.be.instanceOf(RpcException), + expect(err).toBeInstanceOf(RpcException), ); }); }); @@ -143,15 +160,17 @@ describe('RpcContextCreator', () => { instance, instance.test, ); - expect(paramtypes).to.be.eql([String]); + expect(paramtypes).toEqual([String]); }); }); describe('createGuardsFn', () => { it('should throw exception when "tryActivate" returns false', () => { const guardsFn = contextCreator.createGuardsFn([null], null!, null!)!; - sinon.stub(guardsConsumer, 'tryActivate').callsFake(async () => false); - guardsFn([]).catch(err => expect(err).to.not.be.undefined); + vi.spyOn(guardsConsumer, 'tryActivate').mockImplementation( + async () => false, + ); + guardsFn([]).catch(err => expect(err).not.toBeUndefined()); }); }); @@ -179,19 +198,19 @@ describe('RpcContextCreator', () => { { index: 2, type: RpcParamtype.CONTEXT, data: 'test' }, { index: 3, type: `key${CUSTOM_ROUTE_ARGS_METADATA}`, data: 'custom' }, ]; - expect(values[0]).to.deep.include(expectedValues[0]); - expect(values[1]).to.deep.include(expectedValues[1]); - expect(values[2]).to.deep.include(expectedValues[2]); + expect(values[0]).toMatchObject(expectedValues[0]); + expect(values[1]).toMatchObject(expectedValues[1]); + expect(values[2]).toMatchObject(expectedValues[2]); }); }); describe('getParamValue', () => { - let consumerApplySpy: sinon.SinonSpy; + let consumerApplySpy: any; const value = 3, metatype = null, - transforms = [{ transform: sinon.spy() }]; + transforms = [{ transform: vi.fn() }]; beforeEach(() => { - consumerApplySpy = sinon.spy(pipesConsumer, 'apply'); + consumerApplySpy = vi.spyOn(pipesConsumer, 'apply'); }); it('should call "consumer.apply"', async () => { await contextCreator.getParamValue( @@ -199,14 +218,14 @@ describe('RpcContextCreator', () => { { metatype, type: RpcParamtype.PAYLOAD, data: null }, transforms, ); - expect(consumerApplySpy.called).to.be.true; + expect(consumerApplySpy).toHaveBeenCalled(); }); }); describe('createPipesFn', () => { describe('when "paramsOptions" is empty', () => { it('returns null', async () => { const pipesFn = contextCreator.createPipesFn([], []); - expect(pipesFn).to.be.null; + expect(pipesFn).toBeNull(); }); }); describe('when "paramsOptions" is not empty', () => { @@ -224,8 +243,284 @@ describe('RpcContextCreator', () => { ], )!; await pipesFn([]); - expect(pipesFn).to.be.a('function'); + expect(pipesFn).toBeTypeOf('function'); }); }); }); + + describe('preRequest hooks', () => { + function makeCreatorWithHooks(hooks: any[]) { + const container: any = new NestContainer(); + const localRpcProxy = new RpcProxy(); + const localExceptionFiltersContext = new ExceptionFiltersContext( + container, + new ApplicationConfig() as any, + ); + vi.spyOn(localRpcProxy, 'create').mockImplementation(a => a); + + const mockConfig = { + getGlobalPreRequestHooks: () => hooks, + } as any; + + return new RpcContextCreator( + localRpcProxy, + localExceptionFiltersContext, + new PipesContextCreator(container) as any, + new PipesConsumer() as any, + new GuardsContextCreator(container) as any, + new GuardsConsumer() as any, + new InterceptorsContextCreator(container) as any, + new InterceptorsConsumer() as any, + mockConfig, + ); + } + + it('should execute preRequest hook before guards', async () => { + const executionOrder: string[] = []; + + const hookFn = (_ctx: any, next: () => Observable) => { + executionOrder.push('hook'); + return next(); + }; + + const creator = makeCreatorWithHooks([hookFn]); + vi.spyOn( + new GuardsContextCreator(new NestContainer() as any), + 'create', + ).mockReturnValue([] as any); + + const localGuardsConsumer = new GuardsConsumer(); + vi.spyOn(localGuardsConsumer, 'tryActivate').mockImplementation( + async () => { + executionOrder.push('guard'); + return true; + }, + ); + + const container: any = new NestContainer(); + const localRpcProxy = new RpcProxy(); + const localExceptionFiltersContext = new ExceptionFiltersContext( + container, + new ApplicationConfig() as any, + ); + vi.spyOn(localRpcProxy, 'create').mockImplementation(a => a); + const mockConfig = { getGlobalPreRequestHooks: () => [hookFn] } as any; + const localGuardsContextCreator = new GuardsContextCreator(container); + vi.spyOn(localGuardsContextCreator, 'create').mockReturnValue([ + { + canActivate: () => { + executionOrder.push('guard'); + return true; + }, + }, + ] as any); + + const hookCreator = new RpcContextCreator( + localRpcProxy, + localExceptionFiltersContext, + new PipesContextCreator(container) as any, + new PipesConsumer() as any, + localGuardsContextCreator as any, + new GuardsConsumer() as any, + new InterceptorsContextCreator(container) as any, + new InterceptorsConsumer() as any, + mockConfig, + ); + + const proxy = hookCreator.create(instance, instance.test, module, 'test'); + const result = await proxy('data'); + if (result && typeof (result as any).subscribe === 'function') { + await new Promise(resolve => { + (result as Observable).subscribe({ + complete: resolve, + error: resolve, + }); + }); + } + + expect(executionOrder[0]).toBe('hook'); + expect(executionOrder[1]).toBe('guard'); + }); + + it('should chain multiple hooks in registration order', async () => { + const order: string[] = []; + + const hook1 = (_ctx: any, next: () => Observable) => { + order.push('hook1'); + return next(); + }; + const hook2 = (_ctx: any, next: () => Observable) => { + order.push('hook2'); + return next(); + }; + + const container: any = new NestContainer(); + const localRpcProxy = new RpcProxy(); + vi.spyOn(localRpcProxy, 'create').mockImplementation(a => a); + const mockConfig = { + getGlobalPreRequestHooks: () => [hook1, hook2], + } as any; + const localGuardsContextCreator = new GuardsContextCreator(container); + vi.spyOn(localGuardsContextCreator, 'create').mockReturnValue([ + { + canActivate: () => { + order.push('guard'); + return true; + }, + }, + ] as any); + + const hookCreator = new RpcContextCreator( + localRpcProxy, + new ExceptionFiltersContext(container, new ApplicationConfig() as any), + new PipesContextCreator(container) as any, + new PipesConsumer() as any, + localGuardsContextCreator as any, + new GuardsConsumer() as any, + new InterceptorsContextCreator(container) as any, + new InterceptorsConsumer() as any, + mockConfig, + ); + + const proxy = hookCreator.create(instance, instance.test, module, 'test'); + const result = await proxy('data'); + if (result && typeof (result as any).subscribe === 'function') { + await new Promise(resolve => { + (result as Observable).subscribe({ + complete: resolve, + error: resolve, + }); + }); + } + + expect(order).toEqual(['hook1', 'hook2', 'guard']); + }); + + it('should not call hook when no hooks are registered (fast-path)', async () => { + const hookFn = vi.fn((_ctx: any, next: () => Observable) => + next(), + ); + const creator = makeCreatorWithHooks([]); + + const guardSpy = vi.spyOn(guardsConsumer, 'tryActivate'); + vi.spyOn(guardsContextCreator, 'create').mockImplementation( + () => [{ canActivate: () => true }] as any, + ); + + contextCreator = new RpcContextCreator( + rpcProxy, + exceptionFiltersContext, + pipesCreator as any, + pipesConsumer as any, + guardsContextCreator as any, + guardsConsumer as any, + new InterceptorsContextCreator(new NestContainer() as any) as any, + new InterceptorsConsumer() as any, + { getGlobalPreRequestHooks: () => [] } as any, + ); + + const proxy = contextCreator.create( + instance, + instance.test, + module, + 'test', + ); + await proxy('data'); + expect(hookFn).not.toHaveBeenCalled(); + expect(guardSpy).toHaveBeenCalled(); + }); + + it('should provide ExecutionContext with getClass() and getHandler() to the hook', async () => { + let capturedContext: any; + + const hookFn = (ctx: any, next: () => Observable) => { + capturedContext = ctx; + return next(); + }; + + const container: any = new NestContainer(); + const localRpcProxy = new RpcProxy(); + vi.spyOn(localRpcProxy, 'create').mockImplementation(a => a); + const mockConfig = { getGlobalPreRequestHooks: () => [hookFn] } as any; + const localGuardsContextCreator = new GuardsContextCreator(container); + vi.spyOn(localGuardsContextCreator, 'create').mockReturnValue([] as any); + + const hookCreator = new RpcContextCreator( + localRpcProxy, + new ExceptionFiltersContext(container, new ApplicationConfig() as any), + new PipesContextCreator(container) as any, + new PipesConsumer() as any, + localGuardsContextCreator as any, + new GuardsConsumer() as any, + new InterceptorsContextCreator(container) as any, + new InterceptorsConsumer() as any, + mockConfig, + ); + + const proxy = hookCreator.create(instance, instance.test, module, 'test'); + const result = await proxy('data'); + if (result && typeof (result as any).subscribe === 'function') { + await new Promise(resolve => { + (result as Observable).subscribe({ + complete: resolve, + error: resolve, + }); + }); + } + + expect(capturedContext).not.toBeUndefined(); + expect(capturedContext.getClass()).toBe(Test); + expect(capturedContext.getHandler()).toBe(instance.test); + expect(capturedContext.getType()).toBe('rpc'); + }); + + it('should simulate ALS context available in guard (AsyncLocalStorage scenario)', async () => { + const store = new Map(); + let correlationIdInGuard: string | undefined; + + const hookFn = (_ctx: any, next: () => Observable) => { + store.set('correlationId', 'test-id-123'); + return next(); + }; + + const container: any = new NestContainer(); + const localRpcProxy = new RpcProxy(); + vi.spyOn(localRpcProxy, 'create').mockImplementation(a => a); + const mockConfig = { getGlobalPreRequestHooks: () => [hookFn] } as any; + const localGuardsContextCreator = new GuardsContextCreator(container); + vi.spyOn(localGuardsContextCreator, 'create').mockReturnValue([ + { + canActivate: () => { + correlationIdInGuard = store.get('correlationId'); + return true; + }, + }, + ] as any); + + const hookCreator = new RpcContextCreator( + localRpcProxy, + new ExceptionFiltersContext(container, new ApplicationConfig() as any), + new PipesContextCreator(container) as any, + new PipesConsumer() as any, + localGuardsContextCreator as any, + new GuardsConsumer() as any, + new InterceptorsContextCreator(container) as any, + new InterceptorsConsumer() as any, + mockConfig, + ); + + const proxy = hookCreator.create(instance, instance.test, module, 'test'); + const result = await proxy('data'); + if (result && typeof (result as any).subscribe === 'function') { + await new Promise(resolve => { + (result as Observable).subscribe({ + complete: resolve, + error: resolve, + }); + }); + } + + expect(correlationIdInGuard).toBe('test-id-123'); + }); + }); }); diff --git a/packages/microservices/test/context/rpc-proxy.spec.ts b/packages/microservices/test/context/rpc-proxy.spec.ts index ccfac6d2d91..8b3a3c26171 100644 --- a/packages/microservices/test/context/rpc-proxy.spec.ts +++ b/packages/microservices/test/context/rpc-proxy.spec.ts @@ -1,43 +1,45 @@ -import { expect } from 'chai'; import { of, throwError } from 'rxjs'; -import * as sinon from 'sinon'; -import { RpcProxy } from '../../context/rpc-proxy'; -import { RpcException } from '../../exceptions/rpc-exception'; -import { RpcExceptionsHandler } from '../../exceptions/rpc-exceptions-handler'; +import { RpcProxy } from '../../context/rpc-proxy.js'; +import { RpcException } from '../../exceptions/rpc-exception.js'; +import { RpcExceptionsHandler } from '../../exceptions/rpc-exceptions-handler.js'; describe('RpcProxy', () => { let routerProxy: RpcProxy; - let handlerMock: sinon.SinonMock; let handler: RpcExceptionsHandler; beforeEach(() => { handler = new RpcExceptionsHandler(); - handlerMock = sinon.mock(handler); routerProxy = new RpcProxy(); }); describe('create', () => { it('should method return thunk', async () => { const proxy = routerProxy.create(async data => of(true), handler); - expect(typeof proxy === 'function').to.be.true; + expect(typeof proxy === 'function').toBe(true); }); it('should method encapsulate callback passed as argument', async () => { - const expectation = handlerMock.expects('handle').once(); + const handleSpy = vi + .spyOn(handler, 'handle') + .mockImplementation(() => {}); const proxy = routerProxy.create(async data => { throw new RpcException('test'); }, handler); await proxy(null); - expectation.verify(); + expect(handleSpy).toHaveBeenCalledOnce(); }); it('should attach "catchError" operator when observable was returned', async () => { - const expectation = handlerMock.expects('handle').once(); + const handleSpy = vi + .spyOn(handler, 'handle') + .mockImplementation(() => {}); const proxy = routerProxy.create(async (client, data) => { return throwError(() => new RpcException('test')); }, handler); (await proxy(null, null)).subscribe({ - error: () => expectation.verify(), + error: () => { + expect(handleSpy).toHaveBeenCalledOnce(); + }, }); }); }); diff --git a/packages/microservices/test/ctx-host/base-rpc-context.spec.ts b/packages/microservices/test/ctx-host/base-rpc-context.spec.ts index 866567657b0..bb85312e8dd 100644 --- a/packages/microservices/test/ctx-host/base-rpc-context.spec.ts +++ b/packages/microservices/test/ctx-host/base-rpc-context.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { BaseRpcContext } from '../../ctx-host/base-rpc.context'; +import { BaseRpcContext } from '../../ctx-host/base-rpc.context.js'; describe('BaseRpcContext', () => { const args = [1, 2, 3]; @@ -10,14 +9,14 @@ describe('BaseRpcContext', () => { }); describe('getArgs', () => { it('should return "args" array', () => { - expect(rpcContext.getArgs()).to.be.eql(args); + expect(rpcContext.getArgs()).toEqual(args); }); }); describe('getArgByIndex', () => { it('should return argument by index', () => { - expect(rpcContext.getArgByIndex(0)).to.be.eql(args[0]); - expect(rpcContext.getArgByIndex(1)).to.be.eql(args[1]); - expect(rpcContext.getArgByIndex(2)).to.be.eql(args[2]); + expect(rpcContext.getArgByIndex(0)).toEqual(args[0]); + expect(rpcContext.getArgByIndex(1)).toEqual(args[1]); + expect(rpcContext.getArgByIndex(2)).toEqual(args[2]); }); }); }); diff --git a/packages/microservices/test/ctx-host/kafka.context.spec.ts b/packages/microservices/test/ctx-host/kafka.context.spec.ts index 11ba7345611..2a103c6c4ed 100644 --- a/packages/microservices/test/ctx-host/kafka.context.spec.ts +++ b/packages/microservices/test/ctx-host/kafka.context.spec.ts @@ -1,10 +1,9 @@ -import { expect } from 'chai'; -import { KafkaContext } from '../../ctx-host'; +import { KafkaContext } from '../../ctx-host/index.js'; import { Consumer, KafkaMessage, Producer, -} from '../../external/kafka.interface'; +} from '../../external/kafka.interface.js'; describe('KafkaContext', () => { const args = [ @@ -31,32 +30,32 @@ describe('KafkaContext', () => { }); describe('getTopic', () => { it('should return topic', () => { - expect(context.getTopic()).to.be.eql(args[2]); + expect(context.getTopic()).toEqual(args[2]); }); }); describe('getPartition', () => { it('should return partition', () => { - expect(context.getPartition()).to.be.eql(args[1]); + expect(context.getPartition()).toEqual(args[1]); }); }); describe('getMessage', () => { it('should return original message', () => { - expect(context.getMessage()).to.be.eql(args[0]); + expect(context.getMessage()).toEqual(args[0]); }); }); describe('getConsumer', () => { it('should return consumer instance', () => { - expect(context.getConsumer()).to.deep.eq({ test: 'consumer' }); + expect(context.getConsumer()).toEqual({ test: 'consumer' }); }); }); describe('getHeartbeat', () => { it('should return heartbeat callback', () => { - expect(context.getHeartbeat()).to.be.eql(args[4]); + expect(context.getHeartbeat()).toEqual(args[4]); }); }); describe('getProducer', () => { it('should return producer instance', () => { - expect(context.getProducer()).to.deep.eq({ test: 'producer' }); + expect(context.getProducer()).toEqual({ test: 'producer' }); }); }); }); diff --git a/packages/microservices/test/ctx-host/mqtt.context.spec.ts b/packages/microservices/test/ctx-host/mqtt.context.spec.ts index b811fa75990..42585dee5b8 100644 --- a/packages/microservices/test/ctx-host/mqtt.context.spec.ts +++ b/packages/microservices/test/ctx-host/mqtt.context.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { MqttContext } from '../../ctx-host'; +import { MqttContext } from '../../ctx-host/index.js'; describe('MqttContext', () => { const args = ['test', { test: true }]; @@ -10,12 +9,12 @@ describe('MqttContext', () => { }); describe('getTopic', () => { it('should return topic', () => { - expect(context.getTopic()).to.be.eql(args[0]); + expect(context.getTopic()).toEqual(args[0]); }); }); describe('getPacket', () => { it('should return packet', () => { - expect(context.getPacket()).to.be.eql(args[1]); + expect(context.getPacket()).toEqual(args[1]); }); }); }); diff --git a/packages/microservices/test/ctx-host/nats.context.spec.ts b/packages/microservices/test/ctx-host/nats.context.spec.ts index 2704ea875f1..ec4e01fc13d 100644 --- a/packages/microservices/test/ctx-host/nats.context.spec.ts +++ b/packages/microservices/test/ctx-host/nats.context.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { NatsContext } from '../../ctx-host'; +import { NatsContext } from '../../ctx-host/index.js'; describe('NatsContext', () => { const args: [string, any] = ['test', {}]; @@ -10,12 +9,12 @@ describe('NatsContext', () => { }); describe('getSubject', () => { it('should return subject', () => { - expect(context.getSubject()).to.be.eql(args[0]); + expect(context.getSubject()).toEqual(args[0]); }); }); describe('getHeaders', () => { it('should return headers', () => { - expect(context.getHeaders()).to.be.eql(args[1]); + expect(context.getHeaders()).toEqual(args[1]); }); }); }); diff --git a/packages/microservices/test/ctx-host/redis.context.spec.ts b/packages/microservices/test/ctx-host/redis.context.spec.ts index 7901b18e6b5..5d859641bfd 100644 --- a/packages/microservices/test/ctx-host/redis.context.spec.ts +++ b/packages/microservices/test/ctx-host/redis.context.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { RedisContext } from '../../ctx-host'; +import { RedisContext } from '../../ctx-host/index.js'; describe('RedisContext', () => { const args = ['test']; @@ -10,7 +9,7 @@ describe('RedisContext', () => { }); describe('getChannel', () => { it('should return original channel', () => { - expect(context.getChannel()).to.be.eql(args[0]); + expect(context.getChannel()).toEqual(args[0]); }); }); }); diff --git a/packages/microservices/test/ctx-host/rmq.context.spec.ts b/packages/microservices/test/ctx-host/rmq.context.spec.ts index 64f637b6790..6983239976d 100644 --- a/packages/microservices/test/ctx-host/rmq.context.spec.ts +++ b/packages/microservices/test/ctx-host/rmq.context.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { RmqContext } from '../../ctx-host'; +import { RmqContext } from '../../ctx-host/index.js'; describe('RmqContext', () => { const args = [{ test: true }, 'test', 'pattern']; @@ -10,17 +9,17 @@ describe('RmqContext', () => { }); describe('getMessage', () => { it('should return original message', () => { - expect(context.getMessage()).to.be.eql(args[0]); + expect(context.getMessage()).toEqual(args[0]); }); }); describe('getChannelRef', () => { it('should return channel reference', () => { - expect(context.getChannelRef()).to.be.eql(args[1]); + expect(context.getChannelRef()).toEqual(args[1]); }); }); describe('getPattern', () => { it('should return pattern', () => { - expect(context.getPattern()).to.be.eql(args[2]); + expect(context.getPattern()).toEqual(args[2]); }); }); }); diff --git a/packages/microservices/test/ctx-host/tcp.context.spec.ts b/packages/microservices/test/ctx-host/tcp.context.spec.ts index 58067f72724..7b5bd0f1038 100644 --- a/packages/microservices/test/ctx-host/tcp.context.spec.ts +++ b/packages/microservices/test/ctx-host/tcp.context.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { TcpContext } from '../../ctx-host'; +import { TcpContext } from '../../ctx-host/index.js'; describe('TcpContext', () => { const args = [{}, 'pattern']; @@ -10,12 +9,12 @@ describe('TcpContext', () => { }); describe('getSubject', () => { it('should return subject', () => { - expect(context.getSocketRef()).to.be.eql(args[0]); + expect(context.getSocketRef()).toEqual(args[0]); }); }); describe('getPattern', () => { it('should return pattern', () => { - expect(context.getPattern()).to.be.eql(args[1]); + expect(context.getPattern()).toEqual(args[1]); }); }); }); diff --git a/packages/microservices/test/decorators/client.decorator.spec.ts b/packages/microservices/test/decorators/client.decorator.spec.ts index 39492ca7e8c..69c16a7b2fc 100644 --- a/packages/microservices/test/decorators/client.decorator.spec.ts +++ b/packages/microservices/test/decorators/client.decorator.spec.ts @@ -1,10 +1,8 @@ -import 'mocha'; -import { expect } from 'chai'; import { CLIENT_METADATA, CLIENT_CONFIGURATION_METADATA, -} from '../../constants'; -import { Client } from '../../decorators/client.decorator'; +} from '../../constants.js'; +import { Client } from '../../decorators/client.decorator.js'; describe('@Client', () => { const pattern = { role: 'test' }; @@ -24,7 +22,7 @@ describe('@Client', () => { 'instance', ); - expect(isClient).to.be.true; - expect(config).to.be.eql(pattern); + expect(isClient).toBe(true); + expect(config).toEqual(pattern); }); }); diff --git a/packages/microservices/test/decorators/ctx.decorator.spec.ts b/packages/microservices/test/decorators/ctx.decorator.spec.ts index 7ca9c9fccbe..327f04131aa 100644 --- a/packages/microservices/test/decorators/ctx.decorator.spec.ts +++ b/packages/microservices/test/decorators/ctx.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { PARAM_ARGS_METADATA } from '../../constants'; -import { Ctx } from '../../decorators'; -import { RpcParamtype } from '../../enums/rpc-paramtype.enum'; +import { PARAM_ARGS_METADATA } from '../../constants.js'; +import { Ctx } from '../../decorators/index.js'; +import { RpcParamtype } from '../../enums/rpc-paramtype.enum.js'; class CtxTest { public test(@Ctx() ctx: any) {} @@ -21,6 +20,6 @@ describe('@Ctx', () => { pipes: [], }, }; - expect(argsMetadata).to.be.eql(expectedMetadata); + expect(argsMetadata).toEqual(expectedMetadata); }); }); diff --git a/packages/microservices/test/decorators/event-pattern.decorator.spec.ts b/packages/microservices/test/decorators/event-pattern.decorator.spec.ts index 13cbb268441..c0ca70e4015 100644 --- a/packages/microservices/test/decorators/event-pattern.decorator.spec.ts +++ b/packages/microservices/test/decorators/event-pattern.decorator.spec.ts @@ -1,11 +1,10 @@ -import { expect } from 'chai'; import { PATTERN_EXTRAS_METADATA, PATTERN_METADATA, TRANSPORT_METADATA, -} from '../../constants'; -import { EventPattern } from '../../decorators/event-pattern.decorator'; -import { Transport } from '../../enums/transport.enum'; +} from '../../constants.js'; +import { EventPattern } from '../../decorators/event-pattern.decorator.js'; +import { Transport } from '../../enums/transport.enum.js'; describe('@EventPattern', () => { const pattern = { role: 'test' }; @@ -25,32 +24,32 @@ describe('@EventPattern', () => { } it(`should enhance method with ${PATTERN_METADATA} metadata`, () => { const metadata = Reflect.getMetadata(PATTERN_METADATA, TestComponent.test); - expect(metadata.length).to.equal(1); - expect(metadata[0]).to.be.eql(pattern); + expect(metadata.length).toBe(1); + expect(metadata[0]).toEqual(pattern); }); it(`should enhance method with ${PATTERN_EXTRAS_METADATA} metadata`, () => { const metadata = Reflect.getMetadata( PATTERN_EXTRAS_METADATA, TestComponent.test, ); - expect(metadata).to.be.deep.equal(extras); + expect(metadata).toEqual(extras); }); it(`should enhance method with last ${PATTERN_METADATA} metadata`, () => { const metadata = Reflect.getMetadata( PATTERN_METADATA, TestComponent.testOnlyThird, ); - expect(metadata.length).to.equal(1); - expect(metadata[0]).to.be.eql(patternSecond); + expect(metadata.length).toBe(1); + expect(metadata[0]).toEqual(patternSecond); }); it(`should enhance method with both ${PATTERN_METADATA} metadata`, () => { const metadata = Reflect.getMetadata( PATTERN_METADATA, TestComponent.testBoth, ); - expect(metadata.length).to.equal(2); - expect(metadata[0]).to.be.eql(patternSecond); - expect(metadata[1]).to.be.eql(patternThird); + expect(metadata.length).toBe(2); + expect(metadata[0]).toEqual(patternSecond); + expect(metadata[1]).toEqual(patternThird); }); describe('decorator overloads', () => { @@ -85,6 +84,28 @@ describe('@EventPattern', () => { public static test() {} } + type TestComponent6EventTypes = { + 'event-pattern-foo': { foo: string }; + 'event-pattern-bar': { bar: number }; + }; + + // Static tests exclusively for type safety: + class TypeTestComponent { + @EventPattern('event-pattern-foo') + testFoo(_event: { foo: string }, _additionalArg: string) {} + + @EventPattern('event-pattern-bar') + testBar(_event: { bar: number }) {} + + // @ts-expect-error -- `foo` should be a string, not a number + @EventPattern('event-pattern-foo') + testFooMismatch(_event: { foo: number }) {} + + // @ts-expect-error -- `_event` should be `{ foo: string; }`, not a string + @EventPattern('event-pattern-foo') + testFooMismatch2(_event: string) {} + } + it(`should enhance method with ${PATTERN_METADATA} metadata`, () => { const [metadataArg] = Reflect.getMetadata( PATTERN_METADATA, @@ -98,9 +119,9 @@ describe('@EventPattern', () => { PATTERN_EXTRAS_METADATA, TestComponent1.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.undefined; - expect(extrasArg).to.be.eql({}); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toBeUndefined(); + expect(extrasArg).toEqual({}); }); it(`should enhance method with ${PATTERN_METADATA}, ${TRANSPORT_METADATA} metadata`, () => { @@ -116,9 +137,9 @@ describe('@EventPattern', () => { PATTERN_EXTRAS_METADATA, TestComponent2.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.eql(Transport.TCP); - expect(extrasArg).to.be.eql({}); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toEqual(Transport.TCP); + expect(extrasArg).toEqual({}); }); it(`should enhance method with ${PATTERN_METADATA}, ${PATTERN_EXTRAS_METADATA} metadata`, () => { @@ -134,9 +155,9 @@ describe('@EventPattern', () => { PATTERN_EXTRAS_METADATA, TestComponent3.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.undefined; - expect(extrasArg).to.be.eql(extras); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toBeUndefined(); + expect(extrasArg).toEqual(extras); }); it(`should enhance method with ${PATTERN_METADATA}, ${TRANSPORT_METADATA} and \ @@ -153,9 +174,9 @@ ${PATTERN_EXTRAS_METADATA} metadata`, () => { PATTERN_EXTRAS_METADATA, TestComponent4.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.eql(Transport.TCP); - expect(extrasArg).to.be.eql(extras); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toEqual(Transport.TCP); + expect(extrasArg).toEqual(extras); }); it(`should merge with existing ${PATTERN_EXTRAS_METADATA} metadata`, () => { @@ -171,9 +192,9 @@ ${PATTERN_EXTRAS_METADATA} metadata`, () => { PATTERN_EXTRAS_METADATA, TestComponent5.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.eql(Transport.TCP); - expect(extrasArg).to.be.eql({ + expect(metadataArg).toEqual(pattern); + expect(transportArg).toEqual(Transport.TCP); + expect(extrasArg).toEqual({ ...additionalExtras, ...extras, }); diff --git a/packages/microservices/test/decorators/message-pattern.decorator.spec.ts b/packages/microservices/test/decorators/message-pattern.decorator.spec.ts index c37c3643cb5..1b9358816ce 100644 --- a/packages/microservices/test/decorators/message-pattern.decorator.spec.ts +++ b/packages/microservices/test/decorators/message-pattern.decorator.spec.ts @@ -1,17 +1,16 @@ -import { expect } from 'chai'; import { PATTERN_EXTRAS_METADATA, PATTERN_METADATA, TRANSPORT_METADATA, -} from '../../constants'; +} from '../../constants.js'; import { GrpcMethod, GrpcMethodStreamingType, GrpcStreamCall, GrpcStreamMethod, MessagePattern, -} from '../../decorators/message-pattern.decorator'; -import { Transport } from '../../enums/transport.enum'; +} from '../../decorators/message-pattern.decorator.js'; +import { Transport } from '../../enums/transport.enum.js'; describe('@MessagePattern', () => { const pattern = { role: 'test' }; @@ -25,14 +24,14 @@ describe('@MessagePattern', () => { PATTERN_METADATA, TestComponent.test, ); - expect(metadata).to.be.eql(pattern); + expect(metadata).toEqual(pattern); }); it(`should enhance method with ${PATTERN_EXTRAS_METADATA} metadata`, () => { const metadata = Reflect.getMetadata( PATTERN_EXTRAS_METADATA, TestComponent.test, ); - expect(metadata).to.be.deep.equal(extras); + expect(metadata).toEqual(extras); }); describe('decorator overloads', () => { @@ -80,9 +79,9 @@ describe('@MessagePattern', () => { PATTERN_EXTRAS_METADATA, TestComponent1.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.undefined; - expect(extrasArg).to.be.eql({}); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toBeUndefined(); + expect(extrasArg).toEqual({}); }); it(`should enhance method with ${PATTERN_METADATA}, ${TRANSPORT_METADATA} metadata`, () => { @@ -98,9 +97,9 @@ describe('@MessagePattern', () => { PATTERN_EXTRAS_METADATA, TestComponent2.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.eql(Transport.TCP); - expect(extrasArg).to.be.eql({}); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toEqual(Transport.TCP); + expect(extrasArg).toEqual({}); }); it(`should enhance method with ${PATTERN_METADATA}, ${PATTERN_EXTRAS_METADATA} metadata`, () => { @@ -116,9 +115,9 @@ describe('@MessagePattern', () => { PATTERN_EXTRAS_METADATA, TestComponent3.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.undefined; - expect(extrasArg).to.be.eql(extras); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toBeUndefined(); + expect(extrasArg).toEqual(extras); }); it(`should enhance method with ${PATTERN_METADATA}, ${TRANSPORT_METADATA} and \ @@ -135,9 +134,9 @@ ${PATTERN_EXTRAS_METADATA} metadata`, () => { PATTERN_EXTRAS_METADATA, TestComponent4.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.eql(Transport.TCP); - expect(extrasArg).to.be.eql(extras); + expect(metadataArg).toEqual(pattern); + expect(transportArg).toEqual(Transport.TCP); + expect(extrasArg).toEqual(extras); }); it(`should merge with existing ${PATTERN_EXTRAS_METADATA} metadata`, () => { @@ -153,9 +152,9 @@ ${PATTERN_EXTRAS_METADATA} metadata`, () => { PATTERN_EXTRAS_METADATA, TestComponent5.test, ); - expect(metadataArg).to.be.eql(pattern); - expect(transportArg).to.be.eql(Transport.TCP); - expect(extrasArg).to.be.eql({ + expect(metadataArg).toEqual(pattern); + expect(transportArg).toEqual(Transport.TCP); + expect(extrasArg).toEqual({ ...additionalExtras, ...extras, }); @@ -178,7 +177,7 @@ describe('@GrpcMethod', () => { it('should derive method and service name', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: TestService.name, rpc: 'Test', streaming: GrpcMethodStreamingType.NO_STREAMING, @@ -188,7 +187,7 @@ describe('@GrpcMethod', () => { it('should derive method', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test2); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: 'TestService2', rpc: 'Test2', streaming: GrpcMethodStreamingType.NO_STREAMING, @@ -198,7 +197,7 @@ describe('@GrpcMethod', () => { it('should override both method and service', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test3); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: 'TestService2', rpc: 'Test2', streaming: GrpcMethodStreamingType.NO_STREAMING, @@ -221,7 +220,7 @@ describe('@GrpcStreamMethod', () => { it('should derive method and service name', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: TestService.name, rpc: 'Test', streaming: GrpcMethodStreamingType.RX_STREAMING, @@ -231,7 +230,7 @@ describe('@GrpcStreamMethod', () => { it('should derive method', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test2); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: 'TestService2', rpc: 'Test2', streaming: GrpcMethodStreamingType.RX_STREAMING, @@ -241,7 +240,7 @@ describe('@GrpcStreamMethod', () => { it('should override both method and service', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test3); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: 'TestService2', rpc: 'Test2', streaming: GrpcMethodStreamingType.RX_STREAMING, @@ -257,7 +256,7 @@ describe('@GrpcStreamMethod', () => { } const service = new TestService(); const result = service.test({}); - expect(result).to.not.have.property('then'); + expect(result).not.toHaveProperty('then'); }); }); @@ -276,7 +275,7 @@ describe('@GrpcStreamCall', () => { it('should derive method and service name', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: TestService.name, rpc: 'Test', streaming: GrpcMethodStreamingType.PT_STREAMING, @@ -286,7 +285,7 @@ describe('@GrpcStreamCall', () => { it('should derive method', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test2); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: 'TestService2', rpc: 'Test2', streaming: GrpcMethodStreamingType.PT_STREAMING, @@ -296,7 +295,7 @@ describe('@GrpcStreamCall', () => { it('should override both method and service', () => { const svc = new TestService(); const [metadata] = Reflect.getMetadata(PATTERN_METADATA, svc.test3); - expect(metadata).to.be.eql({ + expect(metadata).toEqual({ service: 'TestService2', rpc: 'Test2', streaming: GrpcMethodStreamingType.PT_STREAMING, diff --git a/packages/microservices/test/decorators/payload.decorator.spec.ts b/packages/microservices/test/decorators/payload.decorator.spec.ts index b8bfa96f51d..bbdc80379ef 100644 --- a/packages/microservices/test/decorators/payload.decorator.spec.ts +++ b/packages/microservices/test/decorators/payload.decorator.spec.ts @@ -1,8 +1,7 @@ import { ValidationPipe } from '@nestjs/common'; -import { expect } from 'chai'; -import { PARAM_ARGS_METADATA } from '../../constants'; -import { Payload } from '../../decorators'; -import { RpcParamtype } from '../../enums/rpc-paramtype.enum'; +import { PARAM_ARGS_METADATA } from '../../constants.js'; +import { Payload } from '../../decorators/index.js'; +import { RpcParamtype } from '../../enums/rpc-paramtype.enum.js'; class MessagePayloadTest { public test(@Payload(ValidationPipe) payload: any) {} @@ -22,6 +21,71 @@ describe('@Payload', () => { pipes: [ValidationPipe], }, }; - expect(argsMetadata).to.be.eql(expectedMetadata); + expect(argsMetadata).toEqual(expectedMetadata); + }); +}); + +describe('@Payload with ParameterDecoratorOptions', () => { + const mockSchema = { + '~standard': { + version: 1 as const, + vendor: 'test', + validate: (v: unknown) => ({ value: v }), + }, + }; + + it('should enhance param with schema when options passed as the only argument', () => { + class Test { + public test(@Payload({ schema: mockSchema }) payload: any) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [], + schema: mockSchema, + }); + }); + + it('should enhance param with pipes when options with pipes passed as the only argument', () => { + class Test { + public test( + @Payload({ schema: mockSchema, pipes: [ValidationPipe] }) payload: any, + ) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [ValidationPipe], + schema: mockSchema, + }); + }); + + it('should enhance param with schema when options passed as second argument with property', () => { + class Test { + public test(@Payload('data', { schema: mockSchema }) data: any) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: 'data', + pipes: [], + schema: mockSchema, + }); + }); + + it('should not confuse a pipe instance with options', () => { + class Test { + public test(@Payload(new ValidationPipe()) payload: any) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key].data).toBeUndefined(); + expect(metadata[key].pipes).toHaveLength(1); + expect(metadata[key].schema).toBeUndefined(); }); }); diff --git a/packages/microservices/test/deserializers/identity.deserializer.spec.ts b/packages/microservices/test/deserializers/identity.deserializer.spec.ts index 10336bb353c..247183a53e7 100644 --- a/packages/microservices/test/deserializers/identity.deserializer.spec.ts +++ b/packages/microservices/test/deserializers/identity.deserializer.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { IdentityDeserializer } from '../../deserializers/identity.deserializer'; +import { IdentityDeserializer } from '../../deserializers/identity.deserializer.js'; describe('IdentityDeserializer', () => { let instance: IdentityDeserializer; @@ -9,7 +8,7 @@ describe('IdentityDeserializer', () => { describe('deserialize', () => { it('should return the value unchanged', () => { const value = {}; - expect(instance.deserialize(value)).to.be.eql(value); + expect(instance.deserialize(value)).toEqual(value); }); }); }); diff --git a/packages/microservices/test/deserializers/incoming-request.deserializer.spec.ts b/packages/microservices/test/deserializers/incoming-request.deserializer.spec.ts index 69007e3adaf..7635f9774a9 100644 --- a/packages/microservices/test/deserializers/incoming-request.deserializer.spec.ts +++ b/packages/microservices/test/deserializers/incoming-request.deserializer.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { IncomingRequestDeserializer } from '../../deserializers/incoming-request.deserializer'; -import { IncomingRequest } from '../../interfaces'; +import { IncomingRequestDeserializer } from '../../deserializers/incoming-request.deserializer.js'; +import { IncomingRequest } from '../../interfaces/index.js'; describe('IncomingRequestDeserializer', () => { let instance: IncomingRequestDeserializer; @@ -15,9 +14,7 @@ describe('IncomingRequestDeserializer', () => { pattern: 'pattern', data: [], }; - expect(instance.deserialize(incomingRequest)).to.be.equal( - incomingRequest, - ); + expect(instance.deserialize(incomingRequest)).toBe(incomingRequest); }); }); describe('otherwise', () => { @@ -29,9 +26,7 @@ describe('IncomingRequestDeserializer', () => { const options = { channel: 'test', }; - expect( - instance.deserialize(externalRequest, options), - ).to.be.deep.equal({ + expect(instance.deserialize(externalRequest, options)).toEqual({ pattern: options.channel, data: externalRequest, }); @@ -39,7 +34,7 @@ describe('IncomingRequestDeserializer', () => { }); describe('when options are undefined', () => { it('should map to proper schema with undefined values', () => { - expect(instance.deserialize({})).to.be.deep.equal({ + expect(instance.deserialize({})).toEqual({ pattern: undefined, data: undefined, }); diff --git a/packages/microservices/test/deserializers/incoming-response.deserializer.spec.ts b/packages/microservices/test/deserializers/incoming-response.deserializer.spec.ts index 94375279275..1cc4b5322d7 100644 --- a/packages/microservices/test/deserializers/incoming-response.deserializer.spec.ts +++ b/packages/microservices/test/deserializers/incoming-response.deserializer.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { IncomingResponseDeserializer } from '../../deserializers/incoming-response.deserializer'; -import { IncomingResponse } from '../../interfaces'; +import { IncomingResponseDeserializer } from '../../deserializers/incoming-response.deserializer.js'; +import { IncomingResponse } from '../../interfaces/index.js'; describe('IncomingResponseDeserializer', () => { let instance: IncomingResponseDeserializer; @@ -18,10 +17,8 @@ describe('IncomingResponseDeserializer', () => { id: '1', err: {}, }; - expect(instance.deserialize(incomingResponse)).to.be.equal( - incomingResponse, - ); - expect(instance.deserialize(errResponse)).to.be.equal(errResponse); + expect(instance.deserialize(incomingResponse)).toBe(incomingResponse); + expect(instance.deserialize(errResponse)).toBe(errResponse); }); }); describe('otherwise', () => { @@ -30,7 +27,7 @@ describe('IncomingResponseDeserializer', () => { id: '1', array: [1, 2, 3], }; - expect(instance.deserialize(externalResponse)).to.be.deep.equal({ + expect(instance.deserialize(externalResponse)).toEqual({ id: externalResponse.id, isDisposed: true, response: externalResponse, diff --git a/packages/microservices/test/deserializers/kafka-response.deserializer.spec.ts b/packages/microservices/test/deserializers/kafka-response.deserializer.spec.ts index 30b3276427d..8680c267d05 100644 --- a/packages/microservices/test/deserializers/kafka-response.deserializer.spec.ts +++ b/packages/microservices/test/deserializers/kafka-response.deserializer.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { KafkaResponseDeserializer } from '../../deserializers/kafka-response.deserializer'; -import { KafkaHeaders } from '../../enums/kafka-headers.enum'; +import { KafkaResponseDeserializer } from '../../deserializers/kafka-response.deserializer.js'; +import { KafkaHeaders } from '../../enums/kafka-headers.enum.js'; describe('KafkaResponseDeserializer', () => { const id = '10'; @@ -19,10 +18,10 @@ describe('KafkaResponseDeserializer', () => { [KafkaHeaders.NEST_ERR]: err, }, }); - expect(packet.id).to.be.equal(id); - expect(packet.err).to.be.equal(err); - expect(packet.isDisposed).to.be.true; - expect(packet.response).to.be.undefined; + expect(packet.id).toBe(id); + expect(packet.err).toBe(err); + expect(packet.isDisposed).toBe(true); + expect(packet.response).toBeUndefined(); }); }); describe('when is disposed header is present', () => { @@ -35,10 +34,10 @@ describe('KafkaResponseDeserializer', () => { }, value, }); - expect(packet.id).to.be.equal(id); - expect(packet.err).to.be.undefined; - expect(packet.isDisposed).to.be.true; - expect(packet.response).to.be.eql(value); + expect(packet.id).toBe(id); + expect(packet.err).toBeUndefined(); + expect(packet.isDisposed).toBe(true); + expect(packet.response).toEqual(value); }); }); }); diff --git a/packages/microservices/test/exceptions/rpc-exception.spec.ts b/packages/microservices/test/exceptions/rpc-exception.spec.ts index 76b3871904a..bb3e673a95b 100644 --- a/packages/microservices/test/exceptions/rpc-exception.spec.ts +++ b/packages/microservices/test/exceptions/rpc-exception.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { RpcException } from '../../exceptions/rpc-exception'; +import { RpcException } from '../../exceptions/rpc-exception.js'; describe('RpcException', () => { describe('when string passed', () => { @@ -7,10 +6,10 @@ describe('RpcException', () => { const instance = new RpcException(error); it('should return error message as string', () => { - expect(instance.getError()).to.be.eql(error); + expect(instance.getError()).toEqual(error); }); it('should set the message property', () => { - expect(instance.message).to.be.eql(error); + expect(instance.message).toEqual(error); }); }); @@ -20,10 +19,10 @@ describe('RpcException', () => { const instance = new RpcException(error); it('should return error as object', () => { - expect(instance.getError()).to.be.eql(error); + expect(instance.getError()).toEqual(error); }); it('should fallback error message to class name', () => { - expect(instance.message).to.be.eql('Rpc Exception'); + expect(instance.message).toEqual('Rpc Exception'); }); }); describe('and message property is not undefined', () => { @@ -31,10 +30,10 @@ describe('RpcException', () => { const instance = new RpcException(error); it('should return error as object', () => { - expect(instance.getError()).to.be.eql(error); + expect(instance.getError()).toEqual(error); }); it('should return error message as the extracted "message" string', () => { - expect(instance.message).to.be.eql(error.message); + expect(instance.message).toEqual(error.message); }); }); }); diff --git a/packages/microservices/test/exceptions/rpc-exceptions-handler.spec.ts b/packages/microservices/test/exceptions/rpc-exceptions-handler.spec.ts index 8765f734619..d0731e0abf3 100644 --- a/packages/microservices/test/exceptions/rpc-exceptions-handler.spec.ts +++ b/packages/microservices/test/exceptions/rpc-exceptions-handler.spec.ts @@ -1,9 +1,7 @@ -import { expect } from 'chai'; import { EMPTY, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import * as sinon from 'sinon'; -import { RpcException } from '../../exceptions/rpc-exception'; -import { RpcExceptionsHandler } from '../../exceptions/rpc-exceptions-handler'; +import { RpcException } from '../../exceptions/rpc-exception.js'; +import { RpcExceptionsHandler } from '../../exceptions/rpc-exceptions-handler.js'; describe('RpcExceptionsHandler', () => { let handler: RpcExceptionsHandler; @@ -13,60 +11,63 @@ describe('RpcExceptionsHandler', () => { }); describe('handle', () => { - it('should method returns expected stream with message when exception is unknown', done => { - const stream$ = handler.handle(new Error(), null!); - stream$ - .pipe( - catchError((err: any) => { - expect(err).to.be.eql({ - status: 'error', - message: 'Internal server error', - }); - done(); - return EMPTY; - }), - ) - .subscribe(() => ({})); - }); - describe('when exception is instance of WsException', () => { - it('should method emit expected status and json object', done => { - const message = { - custom: 'Unauthorized', - }; - const stream$ = handler.handle(new RpcException(message), null!); + it('should method returns expected stream with message when exception is unknown', () => + new Promise(done => { + const stream$ = handler.handle(new Error(), null!); stream$ .pipe( catchError((err: any) => { - expect(err).to.be.eql(message); + expect(err).toEqual({ + status: 'error', + message: 'Internal server error', + }); done(); return EMPTY; }), ) .subscribe(() => ({})); - }); - it('should method emit expected status and transform message to json', done => { - const message = 'Unauthorized'; + })); + describe('when exception is instance of WsException', () => { + it('should method emit expected status and json object', () => + new Promise(done => { + const message = { + custom: 'Unauthorized', + }; + const stream$ = handler.handle(new RpcException(message), null!); + stream$ + .pipe( + catchError((err: any) => { + expect(err).toEqual(message); + done(); + return EMPTY; + }), + ) + .subscribe(() => ({})); + })); + it('should method emit expected status and transform message to json', () => + new Promise(done => { + const message = 'Unauthorized'; - const stream$ = handler.handle(new RpcException(message), null!); - stream$ - .pipe( - catchError((err: any) => { - expect(err).to.be.eql({ message, status: 'error' }); - done(); - return EMPTY; - }), - ) - .subscribe(() => ({})); - }); + const stream$ = handler.handle(new RpcException(message), null!); + stream$ + .pipe( + catchError((err: any) => { + expect(err).toEqual({ message, status: 'error' }); + done(); + return EMPTY; + }), + ) + .subscribe(() => ({})); + })); }); describe('when "invokeCustomFilters" returns observable', () => { const observable$ = of(true); beforeEach(() => { - sinon.stub(handler, 'invokeCustomFilters').returns(observable$); + vi.spyOn(handler, 'invokeCustomFilters').mockReturnValue(observable$); }); it('should return observable', () => { const result = handler.handle(new RpcException(''), null!); - expect(result).to.be.eql(observable$); + expect(result).toEqual(observable$); }); }); }); @@ -74,16 +75,16 @@ describe('RpcExceptionsHandler', () => { const filters = ['test', 'test2']; it('should set custom filters', () => { handler.setCustomFilters(filters as any); - expect((handler as any).filters).to.be.eql(filters); + expect((handler as any).filters).toEqual(filters); }); it('should throw exception when passed argument is not an array', () => { - expect(() => handler.setCustomFilters(null!)).to.throw(); + expect(() => handler.setCustomFilters(null!)).toThrow(); }); }); describe('invokeCustomFilters', () => { describe('when filters array is empty', () => { it('should return identity', () => { - expect(handler.invokeCustomFilters(null, null!)).to.be.null; + expect(handler.invokeCustomFilters(null, null!)).toBeNull(); }); }); describe('when filters array is not empty', () => { @@ -91,7 +92,7 @@ describe('RpcExceptionsHandler', () => { class TestException {} beforeEach(() => { - funcSpy = sinon.spy(); + funcSpy = vi.fn(); }); describe('when filter exists in filters array', () => { beforeEach(() => { @@ -100,26 +101,28 @@ describe('RpcExceptionsHandler', () => { }); it('should call funcSpy', () => { handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.false; + expect(funcSpy).toHaveBeenCalled(); }); it('should call funcSpy with exception and response passed as an arguments', () => { const exception = new TestException(); handler.invokeCustomFilters(exception, null!); - expect(funcSpy.calledWith(exception)).to.be.true; + expect(funcSpy).toHaveBeenCalledWith(exception, null); }); it('should return stream', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .not.null; + expect( + handler.invokeCustomFilters(new TestException(), null!), + ).not.toBeNull(); }); }); describe('when filter does not exists in filters array', () => { it('should not call funcSpy', () => { handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.true; + expect(funcSpy).not.toHaveBeenCalled(); }); it('should return null', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .null; + expect( + handler.invokeCustomFilters(new TestException(), null!), + ).toBeNull(); }); }); }); diff --git a/packages/microservices/test/factories/rpc-params-factory.spec.ts b/packages/microservices/test/factories/rpc-params-factory.spec.ts index 40658077689..1261007682e 100644 --- a/packages/microservices/test/factories/rpc-params-factory.spec.ts +++ b/packages/microservices/test/factories/rpc-params-factory.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { RpcParamtype } from '../../enums/rpc-paramtype.enum'; -import { RpcParamsFactory } from '../../factories/rpc-params-factory'; +import { RpcParamtype } from '../../enums/rpc-paramtype.enum.js'; +import { RpcParamsFactory } from '../../factories/rpc-params-factory.js'; describe('RpcParamsFactory', () => { let factory: RpcParamsFactory; @@ -17,32 +16,30 @@ describe('RpcParamsFactory', () => { it('should return a message payload object', () => { expect( factory.exchangeKeyForValue(RpcParamtype.PAYLOAD, null!, args), - ).to.be.eql(payload); + ).toEqual(payload); }); it('should return a message payload object with parameter extraction', () => { expect( factory.exchangeKeyForValue(RpcParamtype.PAYLOAD, 'data', args), - ).to.be.eql(payload.data); + ).toEqual(payload.data); }); }); describe(`RpcParamtype.CONTEXT`, () => { it('should return a ctx object', () => { expect( factory.exchangeKeyForValue(RpcParamtype.CONTEXT, null!, args), - ).to.be.eql(ctx); + ).toEqual(ctx); }); }); }); describe('when key is not available', () => { it('should return null', () => { - expect(factory.exchangeKeyForValue(-1, null!, [])).to.be.eql(null); + expect(factory.exchangeKeyForValue(-1, null!, [])).toEqual(null); }); }); describe('when args are not available', () => { it('should return null', () => { - expect(factory.exchangeKeyForValue(null!, null!, null!)).to.be.eql( - null, - ); + expect(factory.exchangeKeyForValue(null!, null!, null!)).toEqual(null); }); }); }); diff --git a/packages/microservices/test/helpers/grpc-helpers.spec.ts b/packages/microservices/test/helpers/grpc-helpers.spec.ts index e8844a1f9dd..4367144606c 100644 --- a/packages/microservices/test/helpers/grpc-helpers.spec.ts +++ b/packages/microservices/test/helpers/grpc-helpers.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { InvalidGrpcPackageDefinitionMissingPackageDefinitionException } from '../../errors/invalid-grpc-package-definition-missing-package-definition.exception'; -import { InvalidGrpcPackageDefinitionMutexException } from '../../errors/invalid-grpc-package-definition-mutex.exception'; -import { getGrpcPackageDefinition } from '../../helpers/grpc-helpers'; +import { InvalidGrpcPackageDefinitionMissingPackageDefinitionException } from '../../errors/invalid-grpc-package-definition-missing-package-definition.exception.js'; +import { InvalidGrpcPackageDefinitionMutexException } from '../../errors/invalid-grpc-package-definition-mutex.exception.js'; +import { getGrpcPackageDefinition } from '../../helpers/grpc-helpers.js'; const grpcProtoLoaderPackage = { loadSync: (a, b) => 'withLoader' }; @@ -15,7 +14,7 @@ describe('getGrpcPackageDefinition', () => { }, grpcProtoLoaderPackage, ), - ).to.throw(InvalidGrpcPackageDefinitionMissingPackageDefinitionException); + ).toThrow(InvalidGrpcPackageDefinitionMissingPackageDefinitionException); }); }); @@ -30,7 +29,7 @@ describe('getGrpcPackageDefinition', () => { }, grpcProtoLoaderPackage, ), - ).to.throw(InvalidGrpcPackageDefinitionMutexException); + ).toThrow(InvalidGrpcPackageDefinitionMutexException); }); }); @@ -44,7 +43,7 @@ describe('getGrpcPackageDefinition', () => { }, grpcProtoLoaderPackage, ), - ).to.not.throw(Error); + ).not.toThrow(Error); }); }); @@ -58,7 +57,7 @@ describe('getGrpcPackageDefinition', () => { }, grpcProtoLoaderPackage, ), - ).to.not.throw(Error); + ).not.toThrow(Error); }); }); }); diff --git a/packages/microservices/test/helpers/kafka-logger.spec.ts b/packages/microservices/test/helpers/kafka-logger.spec.ts index 844d35d7a5c..e80f521c024 100644 --- a/packages/microservices/test/helpers/kafka-logger.spec.ts +++ b/packages/microservices/test/helpers/kafka-logger.spec.ts @@ -1,7 +1,5 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { logLevel } from '../../external/kafka.interface'; -import { KafkaLogger } from '../../helpers/kafka-logger'; +import { logLevel } from '../../external/kafka.interface.js'; +import { KafkaLogger } from '../../helpers/kafka-logger.js'; const namespace = 'namespace'; const label = 'label'; @@ -15,17 +13,17 @@ const entry = { describe('KafkaLogger', () => { let kafkaLogger: any; - let error: sinon.SinonSpy; - let warn: sinon.SinonSpy; - let log: sinon.SinonSpy; - let debug: sinon.SinonSpy; + let error: ReturnType; + let warn: ReturnType; + let log: ReturnType; + let debug: ReturnType; beforeEach(() => { // set - error = sinon.spy(); - warn = sinon.spy(); - log = sinon.spy(); - debug = sinon.spy(); + error = vi.fn(); + warn = vi.fn(); + log = vi.fn(); + debug = vi.fn(); kafkaLogger = KafkaLogger({ error, @@ -43,8 +41,8 @@ describe('KafkaLogger', () => { log: entry, }); - expect(error.calledOnce).to.be.true; - expect(error.args[0][0]).to.eq( + expect(error).toHaveBeenCalledOnce(); + expect(error.mock.calls[0][0]).toBe( 'label [namespace] message {"other":{"stuff":"here"}}', ); }); @@ -57,7 +55,7 @@ describe('KafkaLogger', () => { log: entry, }); - expect(error.calledOnce).to.be.true; + expect(error).toHaveBeenCalledOnce(); }); it('warn', () => { @@ -68,7 +66,7 @@ describe('KafkaLogger', () => { log: entry, }); - expect(warn.calledOnce).to.be.true; + expect(warn).toHaveBeenCalledOnce(); }); it('info', () => { @@ -79,7 +77,7 @@ describe('KafkaLogger', () => { log: entry, }); - expect(log.calledOnce).to.be.true; + expect(log).toHaveBeenCalledOnce(); }); it('debug', () => { @@ -90,6 +88,6 @@ describe('KafkaLogger', () => { log: entry, }); - expect(debug.calledOnce).to.be.true; + expect(debug).toHaveBeenCalledOnce(); }); }); diff --git a/packages/microservices/test/helpers/kafka-parser.spec.ts b/packages/microservices/test/helpers/kafka-parser.spec.ts index e4f8babcdde..96850ba8297 100644 --- a/packages/microservices/test/helpers/kafka-parser.spec.ts +++ b/packages/microservices/test/helpers/kafka-parser.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { KafkaHeaders } from '../../enums/kafka-headers.enum'; -import { KafkaParser } from '../../helpers/kafka-parser'; +import { KafkaHeaders } from '../../enums/kafka-headers.enum.js'; +import { KafkaParser } from '../../helpers/kafka-parser.js'; describe('KafkaParser', () => { describe('parse', () => { @@ -15,7 +14,7 @@ describe('KafkaParser', () => { kafkaParser.parse({ value: undefined, }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: null, }); @@ -26,7 +25,7 @@ describe('KafkaParser', () => { kafkaParser.parse({ value: null, }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: null, }); @@ -37,7 +36,7 @@ describe('KafkaParser', () => { kafkaParser.parse({ value: Buffer.from('string'), }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: 'string', }); @@ -49,7 +48,7 @@ describe('KafkaParser', () => { kafkaParser.parse({ value: Buffer.from(kafkaSchemaPreambleWithSchemaId), }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: Buffer.from(kafkaSchemaPreambleWithSchemaId), }); @@ -60,7 +59,7 @@ describe('KafkaParser', () => { kafkaParser.parse({ value: Buffer.from('12345'), }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: '12345', }); @@ -73,7 +72,7 @@ describe('KafkaParser', () => { kafkaParser.parse({ value: Buffer.from(long), }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: long, }); @@ -84,7 +83,7 @@ describe('KafkaParser', () => { kafkaParser.parse({ value: Buffer.from(JSON.stringify({ prop: 'value' })), }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: { prop: 'value', @@ -98,7 +97,7 @@ describe('KafkaParser', () => { value: Buffer.from(JSON.stringify({ prop: 'value' })), key: Buffer.from('1'), }), - ).to.deep.eq({ + ).toEqual({ headers: {}, key: '1', value: { @@ -116,7 +115,7 @@ describe('KafkaParser', () => { value: Buffer.from(JSON.stringify({ prop: 'value' })), key: Buffer.from('1'), }), - ).to.deep.eq({ + ).toEqual({ key: '1', value: { prop: 'value', @@ -144,11 +143,11 @@ describe('KafkaParser', () => { [KafkaHeaders.CORRELATION_ID]: 'correlation-id', }, }; - expect(kafkaParser.parse(message)).to.deep.eq(expectedParsedMessage); + expect(kafkaParser.parse(message)).toEqual(expectedParsedMessage); // Parse message again and verify it still works correctly - expect(kafkaParser.parse(message)).to.deep.eq(expectedParsedMessage); + expect(kafkaParser.parse(message)).toEqual(expectedParsedMessage); // Verify message was not modified - expect(message).to.deep.eq({ + expect(message).toEqual({ headers: { [KafkaHeaders.CORRELATION_ID]: Buffer.from('correlation-id'), }, diff --git a/packages/microservices/test/helpers/kafka-reply-partition-assigner.spec.ts b/packages/microservices/test/helpers/kafka-reply-partition-assigner.spec.ts index 1004deb5c18..8272579446f 100644 --- a/packages/microservices/test/helpers/kafka-reply-partition-assigner.spec.ts +++ b/packages/microservices/test/helpers/kafka-reply-partition-assigner.spec.ts @@ -1,24 +1,22 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; import * as Kafka from 'kafkajs'; -import { KafkaReplyPartitionAssigner } from '../../helpers/kafka-reply-partition-assigner'; -import { ClientKafka } from '../../client/client-kafka'; +import { ClientKafka } from '../../client/client-kafka.js'; +import { KafkaReplyPartitionAssigner } from '../../helpers/kafka-reply-partition-assigner.js'; describe('kafka reply partition assigner', () => { let cluster, topics, metadata, assigner, client; - let getConsumerAssignments: sinon.SinonSpy; - let getPreviousAssignment: sinon.SinonSpy; + let getConsumerAssignments: ReturnType; + let getPreviousAssignment: ReturnType; - beforeEach(() => { + beforeEach(async () => { metadata = {}; cluster = { findTopicPartitionMetadata: topic => metadata[topic] }; client = new ClientKafka({}); assigner = new KafkaReplyPartitionAssigner(client, { cluster }); topics = ['topic-A', 'topic-B']; - getConsumerAssignments = sinon.spy(client, 'getConsumerAssignments'); - getPreviousAssignment = sinon.spy(assigner, 'getPreviousAssignment'); + getConsumerAssignments = vi.spyOn(client, 'getConsumerAssignments'); + getPreviousAssignment = vi.spyOn(assigner, 'getPreviousAssignment'); // reset previous assignments client.consumerAssignments = {}; @@ -87,7 +85,7 @@ describe('kafka reply partition assigner', () => { const assignment = await assigner.assign({ members, topics }); - expect(assignment).to.deep.equal([ + expect(assignment).toEqual([ { memberId: 'member-1', memberAssignment: Kafka.AssignerProtocol.MemberAssignment.encode({ @@ -207,7 +205,7 @@ describe('kafka reply partition assigner', () => { const assignment = await assigner.assign({ members, topics }); - expect(assignment).to.deep.equal([ + expect(assignment).toEqual([ { memberId: 'member-1', memberAssignment: Kafka.AssignerProtocol.MemberAssignment.encode({ @@ -257,19 +255,19 @@ describe('kafka reply partition assigner', () => { }); describe('protocol', () => { - it('returns the assigner name and metadata', () => { + it('returns the assigner name and metadata', async () => { // set previous assignments client.consumerAssignments = { 'topic-A': 0, 'topic-B': 1, }; - const protocol = assigner.protocol({ topics }); + const protocol = await assigner.protocol({ topics }); - expect(getPreviousAssignment.calledOnce).to.be.true; - expect(getConsumerAssignments.calledOnce).to.be.true; + expect(getPreviousAssignment).toHaveBeenCalledOnce(); + expect(getConsumerAssignments).toHaveBeenCalledOnce(); - expect(protocol).to.deep.equal({ + expect(protocol).toEqual({ name: assigner.name, metadata: Kafka.AssignerProtocol.MemberMetadata.encode({ version: assigner.version, diff --git a/packages/microservices/test/json-socket/connection.spec.ts b/packages/microservices/test/json-socket/connection.spec.ts index 195788c84e0..29fd70a9d14 100644 --- a/packages/microservices/test/json-socket/connection.spec.ts +++ b/packages/microservices/test/json-socket/connection.spec.ts @@ -1,193 +1,208 @@ -import { expect } from 'chai'; import { AddressInfo, createServer, Socket } from 'net'; -import { TcpEventsMap } from '../../events/tcp.events'; -import { JsonSocket } from '../../helpers/json-socket'; -import { longPayload } from './data/long-payload-with-special-chars'; -import * as helpers from './helpers'; -import { ip } from './helpers'; + +import { JsonSocket } from '../../helpers/json-socket.js'; +import { longPayload } from './data/long-payload-with-special-chars.js'; +import * as helpers from './helpers.js'; +import { ip } from './helpers.js'; const MESSAGE_EVENT = 'message'; describe('JsonSocket connection', () => { - it('should connect, send and receive message', done => { - helpers.createServerAndClient( - (error, server, clientSocket, serverSocket) => { - if (error) { - return done(error); - } - - expect(clientSocket!['isClosed']).to.be.false; - expect(serverSocket!['isClosed']).to.be.false; - - Promise.all([ - new Promise(callback => { - clientSocket!.sendMessage({ type: 'ping' }, callback); - }), - new Promise(callback => { - clientSocket!.on(MESSAGE_EVENT, (message: string) => { - expect(message).to.deep.equal({ type: 'pong' }); - callback(); - }); - }), - new Promise(callback => { - serverSocket!.on(MESSAGE_EVENT, (message: string) => { - expect(message).to.deep.equal({ type: 'ping' }); - serverSocket!.sendMessage({ type: 'pong' }, callback); - }); - }), - ]) - .then(() => { - expect(clientSocket!['isClosed']).to.equal(false); - expect(serverSocket!['isClosed']).to.equal(false); - clientSocket!.end(); - server!.close(done); - }) - .catch(e => done(e)); - }, - ); - }); - - it('should send long messages with special characters without issues', done => { - helpers.createServerAndClient((err, server, clientSocket, serverSocket) => { - if (err) { - return done(err); - } - expect(clientSocket!['isClosed']).to.equal(false); - expect(serverSocket!['isClosed']).to.equal(false); - Promise.all([ - new Promise(callback => { - clientSocket!.sendMessage(longPayload, callback); - }), - new Promise(callback => { - clientSocket!.on(MESSAGE_EVENT, (message: { type: 'pong' }) => { - expect(message).to.deep.equal({ type: 'pong' }); - callback(); - }); - }), - new Promise(callback => { - serverSocket!.on(MESSAGE_EVENT, (message: { type: 'pong' }) => { - expect(message).to.deep.equal(longPayload); - serverSocket!.sendMessage({ type: 'pong' }, callback); - }); - }), - ]) - .then(() => { - expect(clientSocket!['isClosed']).to.equal(false); - expect(serverSocket!['isClosed']).to.equal(false); - clientSocket!.end(); - server!.close(done); - }) - .catch(e => done(e)); - }); - }); - - it('should send multiple messages', done => { - helpers.createServerAndClient((err, server, clientSocket, serverSocket) => { - if (err) { - return done(err); - } - Promise.all([ - new Promise(callback => - Promise.all( - helpers - .range(1, 100) - .map( - i => - new Promise(resolve => - clientSocket!.sendMessage({ number: i }, resolve), - ), - ), - ).then(_ => callback()), - ), - new Promise(callback => { - let lastNumber = 0; - serverSocket!.on(MESSAGE_EVENT, (message: { number: number }) => { - expect(message.number).to.deep.equal(lastNumber + 1); - lastNumber = message.number; - if (lastNumber === 100) { - callback(); - } - }); - }), - ]) - .then(() => { - clientSocket!.end(); - server!.close(done); - }) - .catch(e => done(e)); - }); - }); - - it('should return true for "closed" when server disconnects', done => { - helpers.createServerAndClient((err, server, clientSocket, serverSocket) => { - if (err) { - return done(err); - } - - new Promise(callback => { - serverSocket!.end(); - setTimeout(callback, 10); - }) - .then( - () => + it('should connect, send and receive message', () => + new Promise(done => { + helpers.createServerAndClient( + (error, server, clientSocket, serverSocket) => { + if (error) { + return done(error); + } + + expect(clientSocket!['isClosed']).toBe(false); + expect(serverSocket!['isClosed']).toBe(false); + + Promise.all([ + new Promise(callback => { + clientSocket!.sendMessage({ type: 'ping' }, callback); + }), new Promise(callback => { - expect(clientSocket!['isClosed']).to.equal(true); - expect(serverSocket!['isClosed']).to.equal(true); - callback(); + clientSocket!.on(MESSAGE_EVENT, (message: string) => { + expect(message).toEqual({ type: 'pong' }); + callback(); + }); }), - ) - .then(() => { - clientSocket!.end(); - server!.close(done); - }) - .catch(e => done(e)); - }); - }); - - it('should return true for "closed" when client disconnects', done => { - helpers.createServerAndClient((err, server, clientSocket, serverSocket) => { - if (err) { - return done(err); - } - - new Promise(callback => { - clientSocket!.end(); - setTimeout(callback, 10); - }) - .then( - () => + new Promise(callback => { + serverSocket!.on(MESSAGE_EVENT, (message: string) => { + expect(message).toEqual({ type: 'ping' }); + serverSocket!.sendMessage({ type: 'pong' }, callback); + }); + }), + ]) + .then(() => { + expect(clientSocket!['isClosed']).toBe(false); + expect(serverSocket!['isClosed']).toBe(false); + clientSocket!.end(); + server!.close(done); + }) + .catch(e => done(e)); + }, + ); + })); + + it('should send long messages with special characters without issues', () => + new Promise(done => { + helpers.createServerAndClient( + (err, server, clientSocket, serverSocket) => { + if (err) { + return done(err); + } + expect(clientSocket!['isClosed']).toBe(false); + expect(serverSocket!['isClosed']).toBe(false); + Promise.all([ new Promise(callback => { - expect(clientSocket!['isClosed']).to.equal(true); - expect(serverSocket!['isClosed']).to.equal(true); - callback(); + clientSocket!.sendMessage(longPayload, callback); }), - ) - .then(() => server!.close(done)) - .catch(e => done(e)); - }); - }); - - it('should return true for "closed" when client (re)connects', done => { - const server = createServer(); - - server.on('listening', () => { - const clientSocket = new JsonSocket(new Socket()); - - server.once('connection', socket => { - const serverSocket = new JsonSocket(socket); - - serverSocket.once('end', () => { - setTimeout(() => { - expect(serverSocket['isClosed']).to.equal(true); - expect(clientSocket['isClosed']).to.equal(true); + new Promise(callback => { + clientSocket!.on(MESSAGE_EVENT, (message: { type: 'pong' }) => { + expect(message).toEqual({ type: 'pong' }); + callback(); + }); + }), + new Promise(callback => { + serverSocket!.on(MESSAGE_EVENT, (message: { type: 'pong' }) => { + expect(message).toEqual(longPayload); + serverSocket!.sendMessage({ type: 'pong' }, callback); + }); + }), + ]) + .then(() => { + expect(clientSocket!['isClosed']).toBe(false); + expect(serverSocket!['isClosed']).toBe(false); + clientSocket!.end(); + server!.close(done); + }) + .catch(e => done(e)); + }, + ); + })); + + it('should send multiple messages', () => + new Promise(done => { + helpers.createServerAndClient( + (err, server, clientSocket, serverSocket) => { + if (err) { + return done(err); + } + Promise.all([ + new Promise(callback => + Promise.all( + helpers + .range(1, 100) + .map( + i => + new Promise(resolve => + clientSocket!.sendMessage({ number: i }, resolve), + ), + ), + ).then(_ => callback()), + ), + new Promise(callback => { + let lastNumber = 0; + serverSocket!.on(MESSAGE_EVENT, (message: { number: number }) => { + expect(message.number).toEqual(lastNumber + 1); + lastNumber = message.number; + if (lastNumber === 100) { + callback(); + } + }); + }), + ]) + .then(() => { + clientSocket!.end(); + server!.close(done); + }) + .catch(e => done(e)); + }, + ); + })); + + it('should return true for "closed" when server disconnects', () => + new Promise(done => { + helpers.createServerAndClient( + (err, server, clientSocket, serverSocket) => { + if (err) { + return done(err); + } - clientSocket.on(TcpEventsMap.CONNECT, () => { - setTimeout(() => { - expect(clientSocket['isClosed']).to.equal(false); + new Promise(callback => { + serverSocket!.end(); + setTimeout(callback, 10); + }) + .then( + () => + new Promise(callback => { + expect(clientSocket!['isClosed']).toBe(true); + expect(serverSocket!['isClosed']).toBe(true); + callback(); + }), + ) + .then(() => { + clientSocket!.end(); + server!.close(done); + }) + .catch(e => done(e)); + }, + ); + })); + + it('should return true for "closed" when client disconnects', () => + new Promise(done => { + helpers.createServerAndClient( + (err, server, clientSocket, serverSocket) => { + if (err) { + return done(err); + } - clientSocket.end(); - server.close(done); - }, 10); + new Promise(callback => { + clientSocket!.end(); + setTimeout(callback, 10); + }) + .then( + () => + new Promise(callback => { + expect(clientSocket!['isClosed']).toBe(true); + expect(serverSocket!['isClosed']).toBe(true); + callback(); + }), + ) + .then(() => server!.close(done)) + .catch(e => done(e)); + }, + ); + })); + + it('should return true for "closed" when client (re)connects', () => + new Promise(done => { + const server = createServer(); + + server.on('listening', () => { + const clientSocket = new JsonSocket(new Socket()); + + server.once('connection', socket => { + const serverSocket = new JsonSocket(socket); + + let serverClosed = false; + let clientClosed = false; + + const onBothClosed = () => { + if (!serverClosed || !clientClosed) return; + + expect(serverSocket['isClosed']).toBe(true); + expect(clientSocket['isClosed']).toBe(true); + + clientSocket.on('connect', () => { + expect(clientSocket['isClosed']).toBe(false); + + clientSocket.end(); + server.close(done); }); const address2 = server.address(); @@ -197,20 +212,28 @@ describe('JsonSocket connection', () => { const port2 = (address2 as AddressInfo).port; clientSocket.connect(port2, ip); - }, 10); - }); + }; - clientSocket.end(); - }); + serverSocket.once('close', () => { + serverClosed = true; + onBothClosed(); + }); + clientSocket.once('close', () => { + clientClosed = true; + onBothClosed(); + }); - const address1 = server.address(); - if (!address1) { - throw new Error('server.address() returned null'); - } - const port1 = (address1 as AddressInfo).port; + clientSocket.end(); + }); - clientSocket.connect(port1, ip); - }); - server.listen(); - }); + const address1 = server.address(); + if (!address1) { + throw new Error('server.address() returned null'); + } + const port1 = (address1 as AddressInfo).port; + + clientSocket.connect(port1, ip); + }); + server.listen(); + })); }); diff --git a/packages/microservices/test/json-socket/helpers.ts b/packages/microservices/test/json-socket/helpers.ts index d09fcf279f0..f48d4b4bc8b 100644 --- a/packages/microservices/test/json-socket/helpers.ts +++ b/packages/microservices/test/json-socket/helpers.ts @@ -4,8 +4,7 @@ import { Server, Socket, } from 'net'; -import { TcpEventsMap } from '../../events/tcp.events'; -import { JsonSocket } from '../../helpers/json-socket'; +import { JsonSocket } from '../../helpers/json-socket.js'; export const ip = '127.0.0.1'; @@ -17,7 +16,7 @@ export function createServer(callback: (err?: any, server?: Server) => void) { callback(null, server); }); - server.on(TcpEventsMap.ERROR, (err: any) => { + server.on('error', (err: any) => { callback(err); }); } @@ -40,7 +39,7 @@ export function createClient( clientSocket.connect(port, ip); - clientSocket.on(TcpEventsMap.ERROR, (err: any) => { + clientSocket.on('error', (err: any) => { callback(err); }); diff --git a/packages/microservices/test/json-socket/listener-chaining.spec.ts b/packages/microservices/test/json-socket/listener-chaining.spec.ts index 24f92061889..f523b66824b 100644 --- a/packages/microservices/test/json-socket/listener-chaining.spec.ts +++ b/packages/microservices/test/json-socket/listener-chaining.spec.ts @@ -1,29 +1,28 @@ -import { expect } from 'chai'; -import { TcpEventsMap } from '../../events/tcp.events'; -import { JsonSocket } from '../../helpers/json-socket'; -import * as helpers from './helpers'; +import { JsonSocket } from '../../helpers/json-socket.js'; +import * as helpers from './helpers.js'; const MESSAGE_EVENT = 'message'; describe('JsonSocket chaining', () => { - it('should return the instance when subscribing to event', done => { - helpers.createServerAndClient((err, server, clientSocket, serverSocket) => { - if (err) { - return done(err); - } + it('should return the instance when subscribing to event', () => + new Promise(done => { + helpers.createServerAndClient( + (err, server, clientSocket, serverSocket) => { + if (err) { + return done(err); + } - expect(clientSocket!.on(MESSAGE_EVENT, () => {})).to.be.instanceof( - JsonSocket, - ); - expect(clientSocket!.on(TcpEventsMap.CONNECT, () => {})).to.deep.equal( - clientSocket, - ); - expect( - clientSocket!.on(MESSAGE_EVENT, () => {}).on('end', () => {}), - ).to.deep.equal(clientSocket); + expect(clientSocket!.on(MESSAGE_EVENT, () => {})).toBeInstanceOf( + JsonSocket, + ); + expect(clientSocket!.on('connect', () => {})).toEqual(clientSocket); + expect( + clientSocket!.on(MESSAGE_EVENT, () => {}).on('end', () => {}), + ).toEqual(clientSocket); - clientSocket!.end(); - server!.close(done); - }); - }); + clientSocket!.end(); + server!.close(done); + }, + ); + })); }); diff --git a/packages/microservices/test/json-socket/max-buffer-size.spec.ts b/packages/microservices/test/json-socket/max-buffer-size.spec.ts index fc97f45bcec..a74adf174ee 100644 --- a/packages/microservices/test/json-socket/max-buffer-size.spec.ts +++ b/packages/microservices/test/json-socket/max-buffer-size.spec.ts @@ -1,9 +1,6 @@ -import { expect } from 'chai'; import { Socket } from 'net'; -import * as sinon from 'sinon'; -import { MaxPacketLengthExceededException } from '../../errors/max-packet-length-exceeded.exception'; -import { TcpEventsMap } from '../../events/tcp.events'; -import { JsonSocket } from '../../helpers/json-socket'; +import { MaxPacketLengthExceededException } from '../../errors/max-packet-length-exceeded.exception.js'; +import { JsonSocket } from '../../helpers/json-socket.js'; const DEFAULT_MAX_BUFFER_SIZE = (512 * 1024 * 1024) / 4; // 512 MBs in characters with 4 bytes per character (32-bit) @@ -11,7 +8,7 @@ describe('JsonSocket maxBufferSize', () => { describe('default maxBufferSize', () => { it('should use default maxBufferSize when not provided', () => { const socket = new JsonSocket(new Socket()); - expect(socket['maxBufferSize']).to.equal(DEFAULT_MAX_BUFFER_SIZE); + expect(socket['maxBufferSize']).toBe(DEFAULT_MAX_BUFFER_SIZE); }); it('should accept data up to default maxBufferSize', () => { @@ -26,7 +23,7 @@ describe('JsonSocket maxBufferSize', () => { expect(() => { socket['handleData'](packet); - }).to.not.throw(); + }).not.toThrow(); }); it('should throw MaxPacketLengthExceededException when exceeding default maxBufferSize', () => { @@ -36,7 +33,7 @@ describe('JsonSocket maxBufferSize', () => { expect(() => { socket['handleData'](packet); - }).to.throw(MaxPacketLengthExceededException); + }).toThrow(MaxPacketLengthExceededException); }); }); @@ -46,7 +43,7 @@ describe('JsonSocket maxBufferSize', () => { const socket = new JsonSocket(new Socket(), { maxBufferSize: customSize, }); - expect(socket['maxBufferSize']).to.equal(customSize); + expect(socket['maxBufferSize']).toBe(customSize); }); it('should accept data up to custom maxBufferSize', () => { @@ -64,7 +61,7 @@ describe('JsonSocket maxBufferSize', () => { expect(() => { socket['handleData'](packet); - }).to.not.throw(); + }).not.toThrow(); }); it('should throw MaxPacketLengthExceededException when exceeding custom maxBufferSize', () => { @@ -77,7 +74,7 @@ describe('JsonSocket maxBufferSize', () => { expect(() => { socket['handleData'](packet); - }).to.throw(MaxPacketLengthExceededException); + }).toThrow(MaxPacketLengthExceededException); }); it('should throw MaxPacketLengthExceededException with correct buffer length', () => { @@ -90,12 +87,14 @@ describe('JsonSocket maxBufferSize', () => { // Total buffer size will be: header length (5) + data length (1100) = 1105 const expectedBufferSize = packet.length; + expect(() => { + socket['handleData'](packet); + }).toThrow(MaxPacketLengthExceededException); + try { socket['handleData'](packet); - expect.fail('Should have thrown MaxPacketLengthExceededException'); } catch (err) { - expect(err).to.be.instanceof(MaxPacketLengthExceededException); - expect(err.message).to.include(String(expectedBufferSize)); + expect(err.message).toContain(String(expectedBufferSize)); } }); }); @@ -117,7 +116,7 @@ describe('JsonSocket maxBufferSize', () => { const exceedingData = 'x'.repeat(customSize); expect(() => { socket['handleData'](exceedingData); - }).to.throw(MaxPacketLengthExceededException); + }).toThrow(MaxPacketLengthExceededException); }); it('should clear buffer after throwing MaxPacketLengthExceededException', () => { @@ -134,17 +133,17 @@ describe('JsonSocket maxBufferSize', () => { // Expected } - expect(socket['buffer']).to.equal(''); + expect(socket['buffer']).toBe(''); }); }); describe('error handling when maxBufferSize exceeded', () => { - it(`should emit ${TcpEventsMap.ERROR} event when maxBufferSize is exceeded`, () => { + it('should emit error event when maxBufferSize is exceeded', () => { const customSize = 100; const socket = new JsonSocket(new Socket(), { maxBufferSize: customSize, }); - const socketEmitSpy: sinon.SinonSpy = sinon.spy( + const socketEmitSpy: ReturnType = vi.spyOn( socket['socket'], 'emit', ); @@ -154,37 +153,37 @@ describe('JsonSocket maxBufferSize', () => { socket['onData'](packet); - expect(socketEmitSpy.called).to.be.true; - expect(socketEmitSpy.calledWith(TcpEventsMap.ERROR)).to.be.true; - socketEmitSpy.restore(); + expect(socketEmitSpy).toHaveBeenCalled(); + expect(socketEmitSpy).toHaveBeenCalledWith('error', expect.any(String)); + socketEmitSpy.mockRestore(); }); - it(`should send a FIN packet when maxBufferSize is exceeded`, () => { + it('should send a FIN packet when maxBufferSize is exceeded', () => { const customSize = 100; const socket = new JsonSocket(new Socket(), { maxBufferSize: customSize, }); - const socketEndSpy = sinon.spy(socket['socket'], 'end'); + const socketEndSpy = vi.spyOn(socket['socket'], 'end'); const largeData = 'x'.repeat(customSize + 1); const packet = Buffer.from(`${largeData.length}#${largeData}`); socket['onData'](packet); - expect(socketEndSpy.calledOnce).to.be.true; - socketEndSpy.restore(); + expect(socketEndSpy).toHaveBeenCalledOnce(); + socketEndSpy.mockRestore(); }); }); describe('edge cases', () => { it('should handle maxBufferSize of 0', () => { const socket = new JsonSocket(new Socket(), { maxBufferSize: 0 }); - expect(socket['maxBufferSize']).to.equal(0); + expect(socket['maxBufferSize']).toBe(0); const packet = '5#"test"'; expect(() => { socket['handleData'](packet); - }).to.throw(MaxPacketLengthExceededException); + }).toThrow(MaxPacketLengthExceededException); }); it('should handle very large custom maxBufferSize', () => { @@ -192,7 +191,7 @@ describe('JsonSocket maxBufferSize', () => { const socket = new JsonSocket(new Socket(), { maxBufferSize: veryLargeSize, }); - expect(socket['maxBufferSize']).to.equal(veryLargeSize); + expect(socket['maxBufferSize']).toBe(veryLargeSize); // Account for header length (number + '#') // For 10MB, header is approximately "10485760#" = 10 characters @@ -204,7 +203,7 @@ describe('JsonSocket maxBufferSize', () => { expect(() => { socket['handleData'](packet); - }).to.not.throw(); + }).not.toThrow(); }); it('should handle maxBufferSize exactly at the limit', () => { @@ -223,7 +222,7 @@ describe('JsonSocket maxBufferSize', () => { // Should not throw when exactly at limit expect(() => { socket['handleData'](packet); - }).to.not.throw(); + }).not.toThrow(); }); }); }); diff --git a/packages/microservices/test/json-socket/message-parsing.spec.ts b/packages/microservices/test/json-socket/message-parsing.spec.ts index e42fa42a57b..99393ba7d44 100644 --- a/packages/microservices/test/json-socket/message-parsing.spec.ts +++ b/packages/microservices/test/json-socket/message-parsing.spec.ts @@ -1,8 +1,5 @@ -import { expect } from 'chai'; import { Socket } from 'net'; -import * as sinon from 'sinon'; -import { TcpEventsMap } from '../../events/tcp.events'; -import { JsonSocket } from '../../helpers/json-socket'; +import { JsonSocket } from '../../helpers/json-socket.js'; const MESSAGE_EVENT = 'message'; @@ -22,89 +19,85 @@ describe('JsonSocket message parsing', () => { it('should parse JSON strings', () => { socket['handleData']('13#"Hello there"'); - expect(messages.length).to.deep.equal(1); - expect(messages[0]).to.deep.equal('Hello there'); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(1); + expect(messages[0]).toEqual('Hello there'); + expect(socket['buffer']).toEqual(''); }); it('should parse JSON numbers', () => { socket['handleData']('5#12.34'); - expect(messages.length).to.deep.equal(1); - expect(messages[0]).to.deep.equal(12.34); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(1); + expect(messages[0]).toEqual(12.34); + expect(socket['buffer']).toEqual(''); }); it('should parse JSON bools', () => { socket['handleData']('4#true'); - expect(messages.length).to.deep.equal(1); - expect(messages[0]).to.deep.equal(true); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(1); + expect(messages[0]).toEqual(true); + expect(socket['buffer']).toEqual(''); }); it('should parse JSON objects', () => { socket['handleData']('17#{"a":"yes","b":9}'); - expect(messages.length).to.deep.equal(1); - expect(messages[0]).to.deep.equal({ a: 'yes', b: 9 }); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(1); + expect(messages[0]).toEqual({ a: 'yes', b: 9 }); + expect(socket['buffer']).toEqual(''); }); it('should parse JSON arrays', () => { socket['handleData']('9#["yes",9]'); - expect(messages.length).to.deep.equal(1); - expect(messages[0]).to.deep.equal(['yes', 9]); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(1); + expect(messages[0]).toEqual(['yes', 9]); + expect(socket['buffer']).toEqual(''); }); it('should parse multiple messages in one packet', () => { socket['handleData']('5#"hey"4#true'); - expect(messages.length).to.deep.equal(2); - expect(messages[0]).to.deep.equal('hey'); - expect(messages[1]).to.deep.equal(true); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(2); + expect(messages[0]).toEqual('hey'); + expect(messages[1]).toEqual(true); + expect(socket['buffer']).toEqual(''); }); it('should parse chunked messages', () => { socket['handleData']('13#"Hel'); socket['handleData']('lo there"'); - expect(messages.length).to.deep.equal(1); - expect(messages[0]).to.deep.equal('Hello there'); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(1); + expect(messages[0]).toEqual('Hello there'); + expect(socket['buffer']).toEqual(''); }); it('should parse chunked and multiple messages', () => { socket['handleData']('13#"Hel'); socket['handleData']('lo there"4#true'); - expect(messages.length).to.deep.equal(2); - expect(messages[0]).to.deep.equal('Hello there'); - expect(messages[1]).to.deep.equal(true); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(2); + expect(messages[0]).toEqual('Hello there'); + expect(messages[1]).toEqual(true); + expect(socket['buffer']).toEqual(''); }); it('should parse chunked messages with multi-byte characters', () => { // 0x33 0x23 0xd8 0x22 0xa9 0x22 = 3#"ة" (U+00629) socket['onData'](Buffer.from([0x33, 0x23, 0x22, 0xd8])); socket['onData'](Buffer.from([0xa9, 0x22])); - expect(messages.length).to.deep.equal(1); - expect(messages[0]).to.deep.equal('ة'); + expect(messages.length).toEqual(1); + expect(messages[0]).toEqual('ة'); }); it('should parse multiple messages with unicode correctly', () => { socket['handleData']('41#"Diese Zeile enthält das Unicode-Zeichen"4#true'); - expect(messages[0]).to.deep.equal( - 'Diese Zeile enthält das Unicode-Zeichen', - ); - expect(messages[1]).to.deep.equal(true); - expect(socket['buffer']).to.deep.equal(''); + expect(messages[0]).toEqual('Diese Zeile enthält das Unicode-Zeichen'); + expect(messages[1]).toEqual(true); + expect(socket['buffer']).toEqual(''); }); it('should parse multiple and chunked messages with unicode correctly', () => { socket['handleData']('41#"Diese Zeile enthält '); socket['handleData']('das Unicode-Zeichen"4#true'); - expect(messages[0]).to.deep.equal( - 'Diese Zeile enthält das Unicode-Zeichen', - ); - expect(messages[1]).to.deep.equal(true); - expect(socket['buffer']).to.deep.equal(''); + expect(messages[0]).toEqual('Diese Zeile enthält das Unicode-Zeichen'); + expect(messages[1]).toEqual(true); + expect(socket['buffer']).toEqual(''); }); describe('Error handling', () => { @@ -118,14 +111,14 @@ describe('JsonSocket message parsing', () => { try { socket['handleData']('4#"Hel'); } catch (err) { - expect([errorMsgNodeAboveV20, errorMsg]).to.include(err.message); + expect([errorMsgNodeAboveV20, errorMsg]).toContain(err.message); } - expect(messages.length).to.deep.equal(0); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(0); + expect(socket['buffer']).toEqual(''); }); - it(`should emit ${TcpEventsMap.ERROR} event on socket`, () => { - const socketEmitSpy: sinon.SinonSpy = sinon.spy( + it('should emit error event on socket', () => { + const socketEmitSpy: ReturnType = vi.spyOn( socket['socket'], 'emit', ); @@ -133,28 +126,24 @@ describe('JsonSocket message parsing', () => { socket['onData'](packet); try { - expect( - socketEmitSpy.calledOnceWithExactly(TcpEventsMap.ERROR, errorMsg), - ).to.be.true; + expect(socketEmitSpy).toHaveBeenCalledWith('error', errorMsg); } catch (err) { - expect( - socketEmitSpy.calledWithExactly( - TcpEventsMap.ERROR, - errorMsgNodeAboveV20, - ), - ).to.be.true; + expect(socketEmitSpy).toHaveBeenCalledWith( + 'error', + errorMsgNodeAboveV20, + ); } finally { - socketEmitSpy.restore(); + socketEmitSpy.mockRestore(); } }); it(`should send a FIN packet`, () => { - const socketEndSpy = sinon.spy(socket['socket'], 'end'); + const socketEndSpy = vi.spyOn(socket['socket'], 'end'); socket['onData'](packet); - expect(socketEndSpy.calledOnce).to.be.true; - socketEndSpy.restore(); + expect(socketEndSpy).toHaveBeenCalledOnce(); + socketEndSpy.mockRestore(); }); }); @@ -167,14 +156,14 @@ describe('JsonSocket message parsing', () => { try { socket['handleData'](packetString); } catch (err) { - expect(err.message).to.deep.equal(errorMsg); + expect(err.message).toEqual(errorMsg); } - expect(messages.length).to.deep.equal(0); - expect(socket['buffer']).to.deep.equal(''); + expect(messages.length).toEqual(0); + expect(socket['buffer']).toEqual(''); }); - it(`should emit ${TcpEventsMap.ERROR} event on socket`, () => { - const socketEmitSpy: sinon.SinonSpy = sinon.spy( + it('should emit error event on socket', () => { + const socketEmitSpy: ReturnType = vi.spyOn( socket['socket'], 'emit', ); @@ -182,23 +171,21 @@ describe('JsonSocket message parsing', () => { socket['onData'](packet); try { - expect( - socketEmitSpy.calledOnceWithExactly(TcpEventsMap.ERROR, errorMsg), - ).to.be.true; + expect(socketEmitSpy).toHaveBeenCalledWith('error', errorMsg); } catch { // Do nothing } finally { - socketEmitSpy.restore(); + socketEmitSpy.mockRestore(); } }); it(`should send a FIN packet`, () => { - const socketEndSpy = sinon.spy(socket['socket'], 'end'); + const socketEndSpy = vi.spyOn(socket['socket'], 'end'); socket['onData'](packet); - expect(socketEndSpy.calledOnce).to.be.true; - socketEndSpy.restore(); + expect(socketEndSpy).toHaveBeenCalledOnce(); + socketEndSpy.mockRestore(); }); }); }); diff --git a/packages/microservices/test/listeners-controller.spec.ts b/packages/microservices/test/listeners-controller.spec.ts index 18fc28dd2b5..698e10655fa 100644 --- a/packages/microservices/test/listeners-controller.spec.ts +++ b/packages/microservices/test/listeners-controller.spec.ts @@ -1,45 +1,43 @@ import { Scope } from '@nestjs/common'; import { ApplicationConfig } from '@nestjs/core'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { Injector } from '@nestjs/core/injector/injector'; -import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { GraphInspector } from '../../core/inspector/graph-inspector'; -import { MetadataScanner } from '../../core/metadata-scanner'; -import { ClientProxyFactory } from '../client'; -import { ClientsContainer } from '../container'; -import { ExceptionFiltersContext } from '../context/exception-filters-context'; -import { RpcContextCreator } from '../context/rpc-context-creator'; -import { Transport } from '../enums/transport.enum'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; +import { NestContainer } from '@nestjs/core/injector/container.js'; +import { Injector } from '@nestjs/core/injector/injector.js'; +import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper.js'; +import { GraphInspector } from '../../core/inspector/graph-inspector.js'; +import { MetadataScanner } from '../../core/metadata-scanner.js'; +import { ClientProxyFactory } from '../client/index.js'; +import { ClientsContainer } from '../container.js'; +import { ExceptionFiltersContext } from '../context/exception-filters-context.js'; +import { RpcContextCreator } from '../context/rpc-context-creator.js'; +import { Transport } from '../enums/transport.enum.js'; import { EventOrMessageListenerDefinition, ListenerMetadataExplorer, -} from '../listener-metadata-explorer'; -import { ListenersController } from '../listeners-controller'; +} from '../listener-metadata-explorer.js'; +import { ListenersController } from '../listeners-controller.js'; describe('ListenersController', () => { let instance: ListenersController, - explorer: sinon.SinonMock, + explorer: any, metadataExplorer: ListenerMetadataExplorer, server: any, serverTCP: any, serverCustom: any, customTransport: symbol, - addSpy: sinon.SinonSpy, - addSpyTCP: sinon.SinonSpy, - addSpyCustom: sinon.SinonSpy, - proxySpy: sinon.SinonSpy, + addSpy: ReturnType, + addSpyTCP: ReturnType, + addSpyCustom: ReturnType, + proxySpy: ReturnType, container: NestContainer, graphInspector: GraphInspector, injector: Injector, rpcContextCreator: RpcContextCreator, exceptionFiltersContext: ExceptionFiltersContext; - before(() => { + beforeAll(() => { metadataExplorer = new ListenerMetadataExplorer(new MetadataScanner()); - explorer = sinon.mock(metadataExplorer); + explorer = metadataExplorer; }); beforeEach(() => { container = new NestContainer(); @@ -49,9 +47,16 @@ describe('ListenersController', () => { container, new ApplicationConfig(), ); - rpcContextCreator = sinon.createStubInstance(RpcContextCreator) as any; - proxySpy = sinon.spy(); - (rpcContextCreator as any).create.callsFake(() => proxySpy); + rpcContextCreator = { + ...Object.fromEntries( + Object.getOwnPropertyNames(RpcContextCreator.prototype).map(m => [ + m, + vi.fn(), + ]), + ), + } as any; + proxySpy = vi.fn(); + (rpcContextCreator as any).create.mockImplementation(() => proxySpy); instance = new ListenersController( new ClientsContainer(), @@ -63,16 +68,16 @@ describe('ListenersController', () => { graphInspector, ); (instance as any).metadataExplorer = metadataExplorer; - addSpy = sinon.spy(); + addSpy = vi.fn(); server = { addHandler: addSpy, }; - addSpyTCP = sinon.spy(); + addSpyTCP = vi.fn(); serverTCP = { addHandler: addSpyTCP, transportId: Transport.TCP, }; - addSpyCustom = sinon.spy(); + addSpyCustom = vi.fn(); customTransport = Symbol(); serverCustom = { addHandler: addSpyCustom, @@ -87,12 +92,14 @@ describe('ListenersController', () => { ]; beforeEach(() => { - sinon.stub(container, 'getModuleByKey').callsFake(() => ({}) as any); + vi.spyOn(container, 'getModuleByKey').mockImplementation( + () => ({}) as any, + ); }); it(`should call "addHandler" method of server for each pattern handler`, () => { - explorer.expects('explore').returns(handlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue(handlers as any); instance.registerPatternHandlers(new InstanceWrapper(), server, ''); - expect(addSpy.calledTwice).to.be.true; + expect(addSpy).toHaveBeenCalledTimes(2); }); it(`should call "addHandler" method of server for each pattern handler with same transport`, () => { const serverHandlers = [ @@ -103,9 +110,11 @@ describe('ListenersController', () => { }, { pattern: 'test2', targetCallback: '2', transport: Transport.KAFKA }, ]; - explorer.expects('explore').returns(serverHandlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue( + serverHandlers as any, + ); instance.registerPatternHandlers(new InstanceWrapper(), serverTCP, ''); - expect(addSpyTCP.calledOnce).to.be.true; + expect(addSpyTCP).toHaveBeenCalledOnce(); }); it(`should call "addHandler" method of server without transportID for each pattern handler with any transport value`, () => { const serverHandlers = [ @@ -116,9 +125,11 @@ describe('ListenersController', () => { transport: Transport.KAFKA, }, ]; - explorer.expects('explore').returns(serverHandlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue( + serverHandlers as any, + ); instance.registerPatternHandlers(new InstanceWrapper(), server, ''); - expect(addSpy.calledTwice).to.be.true; + expect(addSpy).toHaveBeenCalledTimes(2); }); it(`should call "addHandler" method of server with transportID for each pattern handler with self transport and without transport`, () => { const serverHandlers = [ @@ -134,14 +145,16 @@ describe('ListenersController', () => { transport: Transport.TCP, }, ]; - explorer.expects('explore').returns(serverHandlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue( + serverHandlers as any, + ); instance.registerPatternHandlers(new InstanceWrapper(), serverTCP, ''); - expect(addSpyTCP.calledTwice).to.be.true; + expect(addSpyTCP).toHaveBeenCalledTimes(2); }); it(`should call "addHandler" method of server with transportID for each pattern handler without transport`, () => { - explorer.expects('explore').returns(handlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue(handlers as any); instance.registerPatternHandlers(new InstanceWrapper(), serverTCP, ''); - expect(addSpyTCP.calledTwice).to.be.true; + expect(addSpyTCP).toHaveBeenCalledTimes(2); }); it(`should call "addHandler" method of server with custom transportID for pattern handler with the same custom token`, () => { const serverHandlers = [ @@ -157,9 +170,11 @@ describe('ListenersController', () => { }, ]; - explorer.expects('explore').returns(serverHandlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue( + serverHandlers as any, + ); instance.registerPatternHandlers(new InstanceWrapper(), serverCustom, ''); - expect(addSpyCustom.calledOnce).to.be.true; + expect(addSpyCustom).toHaveBeenCalledOnce(); }); it(`should call "addHandler" method of server with extras data`, () => { const serverHandlers = [ @@ -169,46 +184,44 @@ describe('ListenersController', () => { extras: { param: 'value' }, }, ]; - explorer.expects('explore').returns(serverHandlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue( + serverHandlers as any, + ); instance.registerPatternHandlers(new InstanceWrapper(), serverTCP, ''); - expect(addSpyTCP.calledOnce).to.be.true; - expect( - addSpyTCP.calledWith( - sinon.match.any, - sinon.match.any, - sinon.match.any, - sinon.match({ param: 'value' }), - ), - ).to.be.true; + expect(addSpyTCP).toHaveBeenCalledOnce(); + expect(addSpyTCP.mock.calls[0][3]).toEqual( + expect.objectContaining({ param: 'value' }), + ); }); describe('when request scoped', () => { it(`should call "addHandler" with deferred proxy`, () => { - explorer.expects('explore').returns(handlers); + vi.spyOn(metadataExplorer, 'explore').mockReturnValue(handlers as any); instance.registerPatternHandlers( new InstanceWrapper({ scope: Scope.REQUEST }), server, '', ); - expect(addSpy.calledTwice).to.be.true; + expect(addSpy).toHaveBeenCalledTimes(2); }); }); }); describe('createRequestScopedHandler', () => { - let handleSpy: sinon.SinonSpy; + let handleSpy: ReturnType; beforeEach(() => { - handleSpy = sinon.spy(); - sinon.stub(exceptionFiltersContext, 'create').callsFake( + handleSpy = vi.fn(); + vi.spyOn(exceptionFiltersContext, 'create').mockImplementation( () => ({ handle: handleSpy, }) as any, ); - sinon - .stub((instance as any).container, 'registerRequestProvider') - .callsFake(() => ({}) as any); + vi.spyOn( + (instance as any).container, + 'registerRequestProvider', + ).mockImplementation(() => ({}) as any); }); describe('when "loadPerContext" resolves', () => { @@ -221,9 +234,9 @@ describe('ListenersController', () => { const wrapper = new InstanceWrapper({ instance: { [methodKey]: {} } }); it('should pass all arguments to the proxy chain', async () => { - sinon - .stub(injector, 'loadPerContext') - .callsFake(() => Promise.resolve({})); + vi.spyOn(injector, 'loadPerContext').mockImplementation(() => + Promise.resolve({}), + ); const handler = instance.createRequestScopedHandler( wrapper, patterns, @@ -233,9 +246,9 @@ describe('ListenersController', () => { ); await handler('data', 'metadata'); - expect(proxySpy.called).to.be.true; - expect(proxySpy.getCall(0).args[0]).to.be.eql('data'); - expect(proxySpy.getCall(0).args[1]).to.be.eql('metadata'); + expect(proxySpy).toHaveBeenCalled(); + expect(proxySpy.mock.calls[0][0]).toEqual('data'); + expect(proxySpy.mock.calls[0][1]).toEqual('metadata'); }); }); @@ -249,7 +262,7 @@ describe('ListenersController', () => { const wrapper = new InstanceWrapper({ instance: { [methodKey]: {} } }); it('should delegate error to exception filters', async () => { - sinon.stub(injector, 'loadPerContext').callsFake(() => { + vi.spyOn(injector, 'loadPerContext').mockImplementation(() => { throw new Error(); }); const handler = instance.createRequestScopedHandler( @@ -261,11 +274,9 @@ describe('ListenersController', () => { ); await handler([]); - expect(handleSpy.called).to.be.true; - expect(handleSpy.getCall(0).args[0]).to.be.instanceOf(Error); - expect(handleSpy.getCall(0).args[1]).to.be.instanceOf( - ExecutionContextHost, - ); + expect(handleSpy).toHaveBeenCalled(); + expect(handleSpy.mock.calls[0][0]).toBeInstanceOf(Error); + expect(handleSpy.mock.calls[0][1]).toBeInstanceOf(ExecutionContextHost); }); }); }); @@ -286,7 +297,7 @@ describe('ListenersController', () => { }; const transportId = Transport.MQTT; - const insertEntrypointDefinitionSpy = sinon.spy( + const insertEntrypointDefinitionSpy = vi.spyOn( graphInspector, 'insertEntrypointDefinition', ); @@ -295,8 +306,8 @@ describe('ListenersController', () => { definition, transportId, ); - expect( - insertEntrypointDefinitionSpy.calledWith({ + expect(insertEntrypointDefinitionSpy).toHaveBeenCalledWith( + { type: 'microservice', methodName: definition.methodKey, className: 'TestCtrl', @@ -308,8 +319,9 @@ describe('ListenersController', () => { isEventHandler: definition.isEventHandler, extras: definition.extras, } as any, - }), - ).to.be.true; + }, + expect.any(String), + ); }); }); @@ -320,7 +332,7 @@ describe('ListenersController', () => { const client = { test: true }; instance.assignClientToInstance(object, propertyKey, client); - expect(object[propertyKey]).to.be.eql(client); + expect(object[propertyKey]).toEqual(client); }); }); @@ -335,17 +347,18 @@ describe('ListenersController', () => { metadata: {}, }, ]; - sinon - .stub((instance as any).metadataExplorer, 'scanForClientHooks') - .callsFake(() => metadata); + vi.spyOn( + (instance as any).metadataExplorer, + 'scanForClientHooks', + ).mockImplementation(() => metadata); - const assignClientToInstanceSpy = sinon.spy( + const assignClientToInstanceSpy = vi.spyOn( instance, 'assignClientToInstance', ); instance.assignClientsToProperties(controller); - expect(assignClientToInstanceSpy.calledOnce).to.be.true; + expect(assignClientToInstanceSpy).toHaveBeenCalledOnce(); }); }); }); diff --git a/packages/microservices/test/listeners-metadata-explorer.spec.ts b/packages/microservices/test/listeners-metadata-explorer.spec.ts index 079beeffbe3..d44658fe00e 100644 --- a/packages/microservices/test/listeners-metadata-explorer.spec.ts +++ b/packages/microservices/test/listeners-metadata-explorer.spec.ts @@ -1,11 +1,9 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { MetadataScanner } from '../../core/metadata-scanner'; -import { Client } from '../decorators/client.decorator'; -import { EventPattern } from '../decorators/event-pattern.decorator'; -import { MessagePattern } from '../decorators/message-pattern.decorator'; -import { Transport } from '../enums/transport.enum'; -import { ListenerMetadataExplorer } from '../listener-metadata-explorer'; +import { MetadataScanner } from '../../core/metadata-scanner.js'; +import { Client } from '../decorators/client.decorator.js'; +import { EventPattern } from '../decorators/event-pattern.decorator.js'; +import { MessagePattern } from '../decorators/message-pattern.decorator.js'; +import { Transport } from '../enums/transport.enum.js'; +import { ListenerMetadataExplorer } from '../listener-metadata-explorer.js'; describe('ListenerMetadataExplorer', () => { const msgPattern = { pattern: 'testMsg' }; @@ -52,16 +50,17 @@ describe('ListenerMetadataExplorer', () => { instance = new ListenerMetadataExplorer(scanner); }); describe('explore', () => { - let getAllMethodNames: sinon.SinonSpy; + let getAllMethodNames: ReturnType; beforeEach(() => { - getAllMethodNames = sinon.spy(scanner, 'getAllMethodNames'); + getAllMethodNames = vi.spyOn(scanner, 'getAllMethodNames'); }); it(`should call "scanFromPrototype" with expected arguments`, () => { const obj = new Test(); instance.explore(obj); - const { args } = getAllMethodNames.getCall(0); - expect(args[0]).to.be.eql(Object.getPrototypeOf(obj)); + expect(getAllMethodNames).toHaveBeenCalledWith( + Object.getPrototypeOf(obj), + ); }); }); describe('exploreMethodMetadata', () => { @@ -75,7 +74,7 @@ describe('ListenerMetadataExplorer', () => { Object.getPrototypeOf(test), 'noPattern', ); - expect(metadata).to.eq(undefined); + expect(metadata).toBeUndefined(); }); describe('@MessagePattern', () => { @@ -85,16 +84,18 @@ describe('ListenerMetadataExplorer', () => { Object.getPrototypeOf(test), 'testMessage', )!; - expect(metadata).to.have.keys([ - 'isEventHandler', - 'methodKey', - 'targetCallback', - 'patterns', - 'transport', - 'extras', - ]); - expect(metadata.patterns.length).to.eql(1); - expect(metadata.patterns[0]).to.eql(msgPattern); + expect(Object.keys(metadata)).toEqual( + expect.arrayContaining([ + 'isEventHandler', + 'methodKey', + 'targetCallback', + 'patterns', + 'transport', + 'extras', + ]), + ); + expect(metadata.patterns.length).toEqual(1); + expect(metadata.patterns[0]).toEqual(msgPattern); }); it(`should return multiple patterns when more than one is declared`, () => { const metadata = instance.exploreMethodMetadata( @@ -102,17 +103,19 @@ describe('ListenerMetadataExplorer', () => { Object.getPrototypeOf(test), 'testMultipleMessage', )!; - expect(metadata).to.have.keys([ - 'isEventHandler', - 'methodKey', - 'targetCallback', - 'patterns', - 'transport', - 'extras', - ]); - expect(metadata.patterns.length).to.eql(2); - expect(metadata.patterns[0]).to.eql(firstMultipleMsgPattern); - expect(metadata.patterns[1]).to.eql(secondMultipleMsgPattern); + expect(Object.keys(metadata)).toEqual( + expect.arrayContaining([ + 'isEventHandler', + 'methodKey', + 'targetCallback', + 'patterns', + 'transport', + 'extras', + ]), + ); + expect(metadata.patterns.length).toEqual(2); + expect(metadata.patterns[0]).toEqual(firstMultipleMsgPattern); + expect(metadata.patterns[1]).toEqual(secondMultipleMsgPattern); }); }); @@ -123,16 +126,18 @@ describe('ListenerMetadataExplorer', () => { Object.getPrototypeOf(test), 'testEvent', )!; - expect(metadata).to.have.keys([ - 'isEventHandler', - 'methodKey', - 'targetCallback', - 'patterns', - 'transport', - 'extras', - ]); - expect(metadata.patterns.length).to.eql(1); - expect(metadata.patterns[0]).to.eql(evtPattern); + expect(Object.keys(metadata)).toEqual( + expect.arrayContaining([ + 'isEventHandler', + 'methodKey', + 'targetCallback', + 'patterns', + 'transport', + 'extras', + ]), + ); + expect(metadata.patterns.length).toEqual(1); + expect(metadata.patterns[0]).toEqual(evtPattern); }); it(`should return multiple patterns when more than one is declared`, () => { const metadata = instance.exploreMethodMetadata( @@ -140,17 +145,19 @@ describe('ListenerMetadataExplorer', () => { Object.getPrototypeOf(test), 'testMultipleEvent', )!; - expect(metadata).to.have.keys([ - 'isEventHandler', - 'methodKey', - 'targetCallback', - 'patterns', - 'transport', - 'extras', - ]); - expect(metadata.patterns.length).to.eql(2); - expect(metadata.patterns[0]).to.eql(firstMultipleEvtPattern); - expect(metadata.patterns[1]).to.eql(secondMultipleEvtPattern); + expect(Object.keys(metadata)).toEqual( + expect.arrayContaining([ + 'isEventHandler', + 'methodKey', + 'targetCallback', + 'patterns', + 'transport', + 'extras', + ]), + ); + expect(metadata.patterns.length).toEqual(2); + expect(metadata.patterns[0]).toEqual(firstMultipleEvtPattern); + expect(metadata.patterns[1]).toEqual(secondMultipleEvtPattern); }); }); }); @@ -159,12 +166,12 @@ describe('ListenerMetadataExplorer', () => { const obj = new Test(); const hooks = [...instance.scanForClientHooks(obj)]; - expect(hooks).to.have.length(2); - expect(hooks[0]).to.deep.eq({ + expect(hooks).toHaveLength(2); + expect(hooks[0]).toEqual({ property: 'client', metadata: clientMetadata, }); - expect(hooks[1]).to.deep.eq({ + expect(hooks[1]).toEqual({ property: 'redisClient', metadata: clientSecMetadata, }); diff --git a/packages/microservices/test/module/clients.module.spec.ts b/packages/microservices/test/module/clients.module.spec.ts index 454b8f2803b..0a6c40c4e5c 100644 --- a/packages/microservices/test/module/clients.module.spec.ts +++ b/packages/microservices/test/module/clients.module.spec.ts @@ -4,12 +4,13 @@ import { Injectable, ValueProvider, } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { ClientProxyFactory } from '../../client'; -import { Transport } from '../../enums'; -import { ClientOptions } from '../../interfaces'; -import { ClientsModule, ClientsModuleOptionsFactory } from '../../module'; +import { ClientProxyFactory } from '../../client/index.js'; +import { Transport } from '../../enums/index.js'; +import { ClientOptions } from '../../interfaces/index.js'; +import { + ClientsModule, + ClientsModuleOptionsFactory, +} from '../../module/index.js'; describe('ClientsModule', () => { let dynamicModule: DynamicModule; @@ -23,14 +24,14 @@ describe('ClientsModule', () => { ]); }); it('should return an expected module ref', () => { - expect(dynamicModule.module).to.be.eql(ClientsModule); + expect(dynamicModule.module).toEqual(ClientsModule); }); it('should return an expected providers array', () => { const provider = dynamicModule.providers!.find( p => 'useValue' in p && p.provide === 'test', ) as ValueProvider; - expect(provider).to.not.be.undefined; - expect(provider.useValue).to.be.deep.eq( + expect(provider).not.toBeUndefined(); + expect(provider.useValue).toEqual( ClientsModule['assignOnAppShutdownHook'](ClientProxyFactory.create({})), ); }); @@ -46,20 +47,20 @@ describe('ClientsModule', () => { it('should return an expected module ref', () => { dynamicModule = ClientsModule.registerAsync([registerOption]); - expect(dynamicModule.module).to.be.eql(ClientsModule); + expect(dynamicModule.module).toEqual(ClientsModule); }); describe('when useFactory', () => { it('should return an expected providers array with useFactory', () => { dynamicModule = ClientsModule.registerAsync([registerOption]); - expect(dynamicModule.imports).to.be.deep.eq([]); - expect(dynamicModule.exports).to.be.eq(dynamicModule.providers); - expect(dynamicModule.providers).to.be.have.length(1); + expect(dynamicModule.imports).toEqual([]); + expect(dynamicModule.exports).toBe(dynamicModule.providers); + expect(dynamicModule.providers).toHaveLength(1); const provider = dynamicModule.providers![0] as FactoryProvider; - expect(provider.provide).to.be.eql('test'); - expect(provider.inject).to.be.deep.eq([]); - expect(provider.useFactory).to.be.an.instanceOf(Function); + expect(provider.provide).toEqual('test'); + expect(provider.inject).toEqual([]); + expect(provider.useFactory).toBeInstanceOf(Function); }); }); @@ -79,14 +80,14 @@ describe('ClientsModule', () => { useClass: ClientOptionService, }; dynamicModule = ClientsModule.registerAsync([useClassOption]); - expect(dynamicModule.imports).to.be.deep.eq([]); - expect(dynamicModule.providers).to.be.have.length(2); + expect(dynamicModule.imports).toEqual([]); + expect(dynamicModule.providers).toHaveLength(2); const classTestProvider = dynamicModule .providers![0] as FactoryProvider; - expect(classTestProvider.provide).to.be.eql('classTest'); - expect(classTestProvider.inject).to.be.deep.eq([ClientOptionService]); - expect(classTestProvider.useFactory).to.be.an.instanceOf(Function); + expect(classTestProvider.provide).toEqual('classTest'); + expect(classTestProvider.inject).toEqual([ClientOptionService]); + expect(classTestProvider.useFactory).toBeInstanceOf(Function); }); it('provider should call "createClientOptions"', async () => { const asyncOptions = { @@ -96,14 +97,14 @@ describe('ClientsModule', () => { asyncOptions as any, ]); const optionsFactory = { - createClientOptions: sinon.spy(), + createClientOptions: vi.fn(), }; try { await (dynamicModule.providers![0] as any).useFactory(optionsFactory); } catch (e) { console.log(e); } - expect(optionsFactory.createClientOptions.called).to.be.true; + expect(optionsFactory.createClientOptions).toHaveBeenCalled(); }); }); @@ -113,11 +114,11 @@ describe('ClientsModule', () => { useExisting: Object, }; dynamicModule = ClientsModule.registerAsync([asyncOptions as any]); - expect(dynamicModule.providers).to.have.length(1); - expect(dynamicModule.imports).to.be.deep.eq([]); + expect(dynamicModule.providers).toHaveLength(1); + expect(dynamicModule.imports).toEqual([]); const classTestProvider = dynamicModule .providers![0] as FactoryProvider; - expect(classTestProvider.useFactory).to.be.an.instanceOf(Function); + expect(classTestProvider.useFactory).toBeInstanceOf(Function); }); }); }); diff --git a/packages/microservices/test/nest-microservice.spec.ts b/packages/microservices/test/nest-microservice.spec.ts index 804f1452234..8ddc026bf9b 100644 --- a/packages/microservices/test/nest-microservice.spec.ts +++ b/packages/microservices/test/nest-microservice.spec.ts @@ -1,31 +1,29 @@ -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; -import { Transport } from '@nestjs/microservices/enums'; -import { AsyncMicroserviceOptions } from '@nestjs/microservices/interfaces'; -import { NestMicroservice } from '@nestjs/microservices/nest-microservice'; -import { Server, ServerTCP } from '@nestjs/microservices/server'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; +import { ApplicationConfig } from '@nestjs/core/application-config.js'; +import { GraphInspector } from '@nestjs/core/inspector/graph-inspector.js'; +import { Transport } from '@nestjs/microservices/enums/index.js'; +import { AsyncMicroserviceOptions } from '@nestjs/microservices/interfaces/index.js'; +import { NestMicroservice } from '@nestjs/microservices/nest-microservice.js'; +import { Server, ServerTCP } from '@nestjs/microservices/server/index.js'; const createMockGraphInspector = (): GraphInspector => ({ - insertOrphanedEnhancer: sinon.stub(), + insertOrphanedEnhancer: vi.fn(), }) as unknown as GraphInspector; const createMockAppConfig = (): ApplicationConfig => ({ - useGlobalFilters: sinon.stub(), - useGlobalPipes: sinon.stub(), - useGlobalGuards: sinon.stub(), - useGlobalInterceptors: sinon.stub(), - setIoAdapter: sinon.stub(), + useGlobalFilters: vi.fn(), + useGlobalPipes: vi.fn(), + useGlobalGuards: vi.fn(), + useGlobalInterceptors: vi.fn(), + setIoAdapter: vi.fn(), }) as unknown as ApplicationConfig; const mockContainer = { - getModuleCompiler: sinon.stub(), + getModuleCompiler: vi.fn(), getModules: () => Object.assign(new Map(), { - addRpcTarget: sinon.spy(), + addRpcTarget: vi.fn(), }), get: () => null, getHttpAdapterHost: () => undefined, @@ -41,7 +39,7 @@ describe('NestMicroservice', () => { }); afterEach(() => { - sinon.restore(); + vi.restoreAllMocks(); }); it('should use ServerFactory if no strategy is provided', () => { @@ -52,15 +50,15 @@ describe('NestMicroservice', () => { mockAppConfig, ); - expect((instance as any).serverInstance).to.be.instanceOf(ServerTCP); + expect((instance as any).serverInstance).toBeInstanceOf(ServerTCP); }); it('should use provided strategy if present in config', () => { const strategy = new (class extends Server { - listen = sinon.spy(); - close = sinon.spy(); - on = sinon.stub(); - unwrap = sinon.stub(); + listen = vi.fn(); + close = vi.fn(); + on = vi.fn(); + unwrap = vi.fn(); })(); const instance = new NestMicroservice( @@ -70,15 +68,15 @@ describe('NestMicroservice', () => { mockAppConfig, ); - expect((instance as any).serverInstance).to.equal(strategy); + expect((instance as any).serverInstance).toBe(strategy); }); it('should use strategy resolved from useFactory config', () => { const strategy = new (class extends Server { - listen = sinon.spy(); - close = sinon.spy(); - on = sinon.stub(); - unwrap = sinon.stub(); + listen = vi.fn(); + close = vi.fn(); + on = vi.fn(); + unwrap = vi.fn(); })(); const asyncConfig: AsyncMicroserviceOptions = { useFactory: () => ({ strategy }), @@ -92,16 +90,16 @@ describe('NestMicroservice', () => { mockAppConfig, ); - expect((instance as any).serverInstance).to.equal(strategy); + expect((instance as any).serverInstance).toBe(strategy); }); it('should call listen() on server when listen() is called', async () => { - const listenSpy = sinon.spy((cb: () => void) => cb()); + const listenSpy = vi.fn((cb: () => void) => cb()); const strategy = new (class extends Server { listen = listenSpy; - close = sinon.spy(); - on = sinon.stub(); - unwrap = sinon.stub(); + close = vi.fn(); + on = vi.fn(); + unwrap = vi.fn(); })(); const instance = new NestMicroservice( @@ -112,15 +110,15 @@ describe('NestMicroservice', () => { ); await instance.listen(); - expect(listenSpy.calledOnce).to.be.true; + expect(listenSpy).toHaveBeenCalledOnce(); }); it('should delegate unwrap() to server', () => { - const unwrapStub = sinon.stub().returns('core'); + const unwrapStub = vi.fn().mockReturnValue('core'); const strategy = new (class extends Server { - listen = sinon.spy(); - close = sinon.spy(); - on = sinon.stub(); + listen = vi.fn(); + close = vi.fn(); + on = vi.fn(); unwrap = unwrapStub; })(); @@ -131,16 +129,16 @@ describe('NestMicroservice', () => { mockAppConfig, ); - expect(instance.unwrap()).to.equal('core'); + expect(instance.unwrap()).toBe('core'); }); it('should delegate on() to server', () => { - const onStub = sinon.stub(); + const onStub = vi.fn(); const strategy = new (class extends Server { - listen = sinon.spy(); - close = sinon.spy(); + listen = vi.fn(); + close = vi.fn(); on = onStub; - unwrap = sinon.stub(); + unwrap = vi.fn(); })(); const instance = new NestMicroservice( @@ -152,6 +150,68 @@ describe('NestMicroservice', () => { const cb = () => {}; instance.on('test:event', cb); - expect(onStub.calledWith('test:event', cb)).to.be.true; + expect(onStub).toHaveBeenCalledWith('test:event', cb); + }); + + describe('registerPreRequestHook', () => { + it('should delegate to applicationConfig.registerPreRequestHook', () => { + const mockConfig = { + ...createMockAppConfig(), + registerPreRequestHook: vi.fn(), + } as unknown as ApplicationConfig; + + const instance = new NestMicroservice( + mockContainer, + { transport: Transport.TCP }, + mockGraphInspector, + mockConfig, + ); + + const hook = (_ctx: any, next: any) => next(); + instance.registerPreRequestHook(hook); + + expect(mockConfig.registerPreRequestHook).toHaveBeenCalledWith(hook); + }); + + it('should warn when called after initialization', () => { + const mockConfig = { + ...createMockAppConfig(), + registerPreRequestHook: vi.fn(), + } as unknown as ApplicationConfig; + + const instance = new NestMicroservice( + mockContainer, + { transport: Transport.TCP }, + mockGraphInspector, + mockConfig, + ); + + const warnSpy = vi.spyOn((instance as any).logger, 'warn'); + (instance as any).isInitialized = true; + + const hook = (_ctx: any, next: any) => next(); + instance.registerPreRequestHook(hook); + + expect(warnSpy).toHaveBeenCalled(); + }); + + it('should return this for fluent API chaining', () => { + const mockConfig = { + ...createMockAppConfig(), + registerPreRequestHook: vi.fn(), + } as unknown as ApplicationConfig; + + const instance = new NestMicroservice( + mockContainer, + { transport: Transport.TCP }, + mockGraphInspector, + mockConfig, + ); + + const hook = (_ctx: any, next: any) => next(); + const result = instance.registerPreRequestHook(hook); + + expect(result).toBe(instance); + }); }); }); diff --git a/packages/microservices/test/serializers/identity.serializer.spec.ts b/packages/microservices/test/serializers/identity.serializer.spec.ts index 2df2fdbe602..e4e3676fc35 100644 --- a/packages/microservices/test/serializers/identity.serializer.spec.ts +++ b/packages/microservices/test/serializers/identity.serializer.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { IdentitySerializer } from '../../serializers/identity.serializer'; +import { IdentitySerializer } from '../../serializers/identity.serializer.js'; describe('IdentitySerializer', () => { let instance: IdentitySerializer; @@ -9,7 +8,7 @@ describe('IdentitySerializer', () => { describe('serialize', () => { it('should return the value unchanged', () => { const value = {}; - expect(instance.serialize(value)).to.be.eql(value); + expect(instance.serialize(value)).toEqual(value); }); }); }); diff --git a/packages/microservices/test/serializers/kafka-request.serializer.spec.ts b/packages/microservices/test/serializers/kafka-request.serializer.spec.ts index ed07a013acf..c85975be539 100644 --- a/packages/microservices/test/serializers/kafka-request.serializer.spec.ts +++ b/packages/microservices/test/serializers/kafka-request.serializer.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { KafkaHeaders } from '../../enums/kafka-headers.enum'; -import { KafkaRequestSerializer } from '../../serializers/kafka-request.serializer'; +import { KafkaHeaders } from '../../enums/kafka-headers.enum.js'; +import { KafkaRequestSerializer } from '../../serializers/kafka-request.serializer.js'; describe('KafkaRequestSerializer', () => { let instance: KafkaRequestSerializer; @@ -9,42 +8,42 @@ describe('KafkaRequestSerializer', () => { }); describe('serialize', () => { it('undefined', async () => { - expect(await instance.serialize(undefined)).to.deep.eq({ + expect(await instance.serialize(undefined)).toEqual({ headers: {}, value: null, }); }); it('null', async () => { - expect(await instance.serialize(null)).to.deep.eq({ + expect(await instance.serialize(null)).toEqual({ headers: {}, value: null, }); }); it('string', async () => { - expect(await instance.serialize('string')).to.deep.eq({ + expect(await instance.serialize('string')).toEqual({ headers: {}, value: 'string', }); }); it('number', async () => { - expect(await instance.serialize(12345)).to.deep.eq({ + expect(await instance.serialize(12345)).toEqual({ headers: {}, value: '12345', }); }); it('buffer', async () => { - expect(await instance.serialize(Buffer.from('buffer'))).to.deep.eq({ + expect(await instance.serialize(Buffer.from('buffer'))).toEqual({ headers: {}, value: Buffer.from('buffer'), }); }); it('array', async () => { - expect(await instance.serialize([1, 2, 3, 4, 5])).to.deep.eq({ + expect(await instance.serialize([1, 2, 3, 4, 5])).toEqual({ headers: {}, value: '[1,2,3,4,5]', }); @@ -55,7 +54,7 @@ describe('KafkaRequestSerializer', () => { await instance.serialize({ prop: 'value', }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: '{"prop":"value"}', }); @@ -69,7 +68,7 @@ describe('KafkaRequestSerializer', () => { } } - expect(await instance.serialize(new Complex())).to.deep.eq({ + expect(await instance.serialize(new Complex())).toEqual({ headers: {}, value: 'complex', }); @@ -85,7 +84,7 @@ describe('KafkaRequestSerializer', () => { class ComplexChild extends ComplexParent {} - expect(await instance.serialize(new ComplexChild())).to.deep.eq({ + expect(await instance.serialize(new ComplexChild())).toEqual({ headers: {}, value: 'complexParent', }); @@ -96,12 +95,10 @@ describe('KafkaRequestSerializer', () => { private readonly name = 'complex'; } - expect(await instance.serialize(new ComplexWithOutToString())).to.deep.eq( - { - headers: {}, - value: '{"name":"complex"}', - }, - ); + expect(await instance.serialize(new ComplexWithOutToString())).toEqual({ + headers: {}, + value: '{"name":"complex"}', + }); }); }); @@ -111,7 +108,7 @@ describe('KafkaRequestSerializer', () => { await instance.serialize({ value: 'string', }), - ).to.deep.eq({ + ).toEqual({ headers: {}, value: 'string', }); @@ -123,7 +120,7 @@ describe('KafkaRequestSerializer', () => { key: '1', value: 'string', }), - ).to.deep.eq({ + ).toEqual({ headers: {}, key: '1', value: 'string', @@ -139,7 +136,7 @@ describe('KafkaRequestSerializer', () => { [KafkaHeaders.CORRELATION_ID]: '1234', }, }), - ).to.deep.eq({ + ).toEqual({ headers: { [KafkaHeaders.CORRELATION_ID]: '1234', }, diff --git a/packages/microservices/test/serializers/mqtt-record.serializer.spec.ts b/packages/microservices/test/serializers/mqtt-record.serializer.spec.ts index 0070904f688..8ff7604a83a 100644 --- a/packages/microservices/test/serializers/mqtt-record.serializer.spec.ts +++ b/packages/microservices/test/serializers/mqtt-record.serializer.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { MqttRecordBuilder } from '../../record-builders'; -import { MqttRecordSerializer } from '../../serializers/mqtt-record.serializer'; +import { MqttRecordBuilder } from '../../record-builders/index.js'; +import { MqttRecordSerializer } from '../../serializers/mqtt-record.serializer.js'; describe('MqttRecordSerializer', () => { let instance: MqttRecordSerializer; @@ -22,7 +21,7 @@ describe('MqttRecordSerializer', () => { pattern: 'pattern', data: mqttMessage, }), - ).to.deep.eq( + ).toEqual( JSON.stringify({ pattern: 'pattern', data: { value: 'string' }, @@ -34,7 +33,7 @@ describe('MqttRecordSerializer', () => { pattern: 'pattern', data: { random: true }, }; - expect(instance.serialize(packet)).to.eq(JSON.stringify(packet)); + expect(instance.serialize(packet)).toBe(JSON.stringify(packet)); }); }); }); diff --git a/packages/microservices/test/serializers/nats-record.serializer.spec.ts b/packages/microservices/test/serializers/nats-record.serializer.spec.ts index b34ed0a0ba8..92584cba85a 100644 --- a/packages/microservices/test/serializers/nats-record.serializer.spec.ts +++ b/packages/microservices/test/serializers/nats-record.serializer.spec.ts @@ -1,9 +1,6 @@ -import { expect } from 'chai'; -import * as nats from 'nats'; -import { NatsRecordBuilder } from '../../record-builders'; -import { NatsRecordSerializer } from '../../serializers/nats-record.serializer'; - -const jsonCodec = nats.JSONCodec(); +import * as nats from '@nats-io/nats-core'; +import { NatsRecordBuilder } from '../../record-builders/index.js'; +import { NatsRecordSerializer } from '../../serializers/nats-record.serializer.js'; describe('NatsRecordSerializer', () => { let instance: NatsRecordSerializer; @@ -12,52 +9,52 @@ describe('NatsRecordSerializer', () => { }); describe('serialize', () => { it('undefined', () => { - expect(instance.serialize({ data: undefined })).to.deep.eq({ + expect(instance.serialize({ data: undefined })).toEqual({ headers: undefined, - data: jsonCodec.encode({ data: undefined }), + data: JSON.stringify({ data: undefined }), }); }); it('null', () => { - expect(instance.serialize({ data: null })).to.deep.eq({ + expect(instance.serialize({ data: null })).toEqual({ headers: undefined, - data: jsonCodec.encode({ data: null }), + data: JSON.stringify({ data: null }), }); }); it('string', () => { - expect(instance.serialize({ data: 'string' })).to.deep.eq({ + expect(instance.serialize({ data: 'string' })).toEqual({ headers: undefined, - data: jsonCodec.encode({ data: 'string' }), + data: JSON.stringify({ data: 'string' }), }); }); it('number', () => { - expect(instance.serialize({ data: 12345 })).to.deep.eq({ + expect(instance.serialize({ data: 12345 })).toEqual({ headers: undefined, - data: jsonCodec.encode({ data: 12345 }), + data: JSON.stringify({ data: 12345 }), }); }); it('buffer', () => { - expect(instance.serialize({ data: Buffer.from('buffer') })).to.deep.eq({ + expect(instance.serialize({ data: Buffer.from('buffer') })).toEqual({ headers: undefined, - data: jsonCodec.encode({ data: Buffer.from('buffer') }), + data: JSON.stringify({ data: Buffer.from('buffer') }), }); }); it('array', () => { - expect(instance.serialize({ data: [1, 2, 3, 4, 5] })).to.deep.eq({ + expect(instance.serialize({ data: [1, 2, 3, 4, 5] })).toEqual({ headers: undefined, - data: jsonCodec.encode({ data: [1, 2, 3, 4, 5] }), + data: JSON.stringify({ data: [1, 2, 3, 4, 5] }), }); }); it('object', () => { const serObject = { prop: 'value' }; - expect(instance.serialize({ data: serObject })).to.deep.eq({ + expect(instance.serialize({ data: serObject })).toEqual({ headers: undefined, - data: jsonCodec.encode({ data: serObject }), + data: JSON.stringify({ data: serObject }), }); }); @@ -72,9 +69,9 @@ describe('NatsRecordSerializer', () => { instance.serialize({ data: natsMessage, }), - ).to.deep.eq({ + ).toEqual({ headers: natsHeaders, - data: jsonCodec.encode({ + data: JSON.stringify({ data: { value: 'string', }, diff --git a/packages/microservices/test/serializers/rmq-record.serializer.spec.ts b/packages/microservices/test/serializers/rmq-record.serializer.spec.ts index b9dc4202863..614ba279fa6 100644 --- a/packages/microservices/test/serializers/rmq-record.serializer.spec.ts +++ b/packages/microservices/test/serializers/rmq-record.serializer.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { RmqRecordBuilder } from '../../record-builders'; -import { RmqRecordSerializer } from '../../serializers/rmq-record.serializer'; +import { RmqRecordBuilder } from '../../record-builders/index.js'; +import { RmqRecordSerializer } from '../../serializers/rmq-record.serializer.js'; describe('RmqRecordSerializer', () => { const pattern = 'test'; @@ -21,7 +20,7 @@ describe('RmqRecordSerializer', () => { pattern, data: rmqMessage, }), - ).to.deep.eq({ + ).toEqual({ pattern, options: { appId: 'app', persistent: true }, data: { value: 'string' }, @@ -33,7 +32,7 @@ describe('RmqRecordSerializer', () => { pattern, data: { random: true }, }; - expect(instance.serialize(packet)).to.eq(packet); + expect(instance.serialize(packet)).toBe(packet); }); }); }); diff --git a/packages/microservices/test/server/server-factory.spec.ts b/packages/microservices/test/server/server-factory.spec.ts index 62e5904556c..8fc72548c62 100644 --- a/packages/microservices/test/server/server-factory.spec.ts +++ b/packages/microservices/test/server/server-factory.spec.ts @@ -1,59 +1,58 @@ -import { expect } from 'chai'; -import { Transport } from '../../enums/transport.enum'; -import { ServerFactory } from '../../server/server-factory'; -import { ServerGrpc } from '../../server/server-grpc'; -import { ServerKafka } from '../../server/server-kafka'; -import { ServerMqtt } from '../../server/server-mqtt'; -import { ServerNats } from '../../server/server-nats'; -import { ServerRedis } from '../../server/server-redis'; -import { ServerRMQ } from '../../server/server-rmq'; -import { ServerTCP } from '../../server/server-tcp'; +import { Transport } from '../../enums/transport.enum.js'; +import { ServerFactory } from '../../server/server-factory.js'; +import { ServerGrpc } from '../../server/server-grpc.js'; +import { ServerKafka } from '../../server/server-kafka.js'; +import { ServerMqtt } from '../../server/server-mqtt.js'; +import { ServerNats } from '../../server/server-nats.js'; +import { ServerRedis } from '../../server/server-redis.js'; +import { ServerRMQ } from '../../server/server-rmq.js'; +import { ServerTCP } from '../../server/server-tcp.js'; describe('ServerFactory', () => { describe('create', () => { it(`should return tcp server by default`, () => { - expect(ServerFactory.create({}) instanceof ServerTCP).to.be.true; + expect(ServerFactory.create({}) instanceof ServerTCP).toBe(true); }); it(`should return redis server`, () => { expect( ServerFactory.create({ transport: Transport.REDIS }) instanceof ServerRedis, - ).to.be.true; + ).toBe(true); }); it(`should return redis server`, () => { expect( ServerFactory.create({ transport: Transport.REDIS }) instanceof ServerRedis, - ).to.be.true; + ).toBe(true); }); it(`should return mqtt server`, () => { expect( ServerFactory.create({ transport: Transport.MQTT }) instanceof ServerMqtt, - ).to.be.true; + ).toBe(true); }); it(`should return nats server`, () => { expect( ServerFactory.create({ transport: Transport.NATS }) instanceof ServerNats, - ).to.be.true; + ).toBe(true); }); it(`should return rmq server`, () => { expect( ServerFactory.create({ transport: Transport.RMQ }) instanceof ServerRMQ, - ).to.be.true; + ).toBe(true); }); it(`should return kafka server`, () => { expect( ServerFactory.create({ transport: Transport.KAFKA }) instanceof ServerKafka, - ).to.be.true; + ).toBe(true); }); it(`should return grpc server`, () => { @@ -62,7 +61,7 @@ describe('ServerFactory', () => { transport: Transport.GRPC, options: { protoPath: '', package: '' }, }) instanceof ServerGrpc, - ).to.be.true; + ).toBe(true); }); it(`should return redis server with specific transport id`, () => { @@ -72,8 +71,8 @@ describe('ServerFactory', () => { }); server.setTransportId(transportId); - expect(server instanceof ServerRedis).to.be.true; - expect(server.transportId === transportId).to.be.true; + expect(server instanceof ServerRedis).toBe(true); + expect(server.transportId === transportId).toBe(true); }); it(`should return mqtt server with specific transport id`, () => { @@ -83,8 +82,8 @@ describe('ServerFactory', () => { }); server.setTransportId(transportId); - expect(server instanceof ServerMqtt).to.be.true; - expect(server.transportId === transportId).to.be.true; + expect(server instanceof ServerMqtt).toBe(true); + expect(server.transportId === transportId).toBe(true); }); it(`should return nats server with specific transport id`, () => { @@ -94,8 +93,8 @@ describe('ServerFactory', () => { }); server.setTransportId(transportId); - expect(server instanceof ServerNats).to.be.true; - expect(server.transportId === transportId).to.be.true; + expect(server instanceof ServerNats).toBe(true); + expect(server.transportId === transportId).toBe(true); }); it(`should return rmq server with specific transport id`, () => { @@ -105,8 +104,8 @@ describe('ServerFactory', () => { }); server.setTransportId(transportId); - expect(server instanceof ServerRMQ).to.be.true; - expect(server.transportId === transportId).to.be.true; + expect(server instanceof ServerRMQ).toBe(true); + expect(server.transportId === transportId).toBe(true); }); it(`should return kafka server with specific transport id`, () => { @@ -116,8 +115,8 @@ describe('ServerFactory', () => { }); server.setTransportId(transportId); - expect(server instanceof ServerKafka).to.be.true; - expect(server.transportId === transportId).to.be.true; + expect(server instanceof ServerKafka).toBe(true); + expect(server.transportId === transportId).toBe(true); }); it(`should return grpc server with specific transport id`, () => { @@ -128,8 +127,8 @@ describe('ServerFactory', () => { }); server.setTransportId(transportId); - expect(server instanceof ServerGrpc).to.be.true; - expect(server.transportId === transportId).to.be.true; + expect(server instanceof ServerGrpc).toBe(true); + expect(server.transportId === transportId).toBe(true); }); }); }); diff --git a/packages/microservices/test/server/server-grpc.spec.ts b/packages/microservices/test/server/server-grpc.spec.ts index 6f1e40888e8..f4034079ca0 100644 --- a/packages/microservices/test/server/server-grpc.spec.ts +++ b/packages/microservices/test/server/server-grpc.spec.ts @@ -1,13 +1,11 @@ import { Logger } from '@nestjs/common'; -import { expect } from 'chai'; import { join } from 'path'; import { ReplaySubject, Subject, throwError } from 'rxjs'; -import * as sinon from 'sinon'; -import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception'; -import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception'; -import * as grpcHelpers from '../../helpers/grpc-helpers'; -import { GrpcMethodStreamingType } from '../../index'; -import { ServerGrpc } from '../../server'; +import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception.js'; +import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception.js'; +import { GrpcMethodStreamingType } from '../../index.js'; +import { ServerGrpc } from '../../server/index.js'; +import { objectToMap } from './utils/object-to-map.js'; const CANCELLED_EVENT = 'cancelled'; @@ -24,7 +22,7 @@ describe('ServerGrpc', () => { beforeEach(() => { server = new ServerGrpc({ - protoPath: join(__dirname, './test.proto'), + protoPath: join(import.meta.dirname, './test.proto'), package: 'test', }); untypedServer = server as any; @@ -33,84 +31,82 @@ describe('ServerGrpc', () => { protoPath: ['test.proto', 'test2.proto'], package: ['test', 'test2'], loader: { - includeDirs: [join(__dirname, '.')], + includeDirs: [join(import.meta.dirname, '.')], }, }); }); describe('listen', () => { - let callback: sinon.SinonSpy; - let bindEventsStub: sinon.SinonStub; + let callback: ReturnType; + let bindEventsStub: ReturnType; beforeEach(() => { - callback = sinon.spy(); - bindEventsStub = sinon - .stub(server, 'bindEvents') - .callsFake(() => ({}) as any); + callback = vi.fn(); + bindEventsStub = vi + .spyOn(server, 'bindEvents') + .mockImplementation(() => ({}) as any); }); it('should call "bindEvents"', async () => { await server.listen(callback); await server.close(); - expect(bindEventsStub.called).to.be.true; + expect(bindEventsStub).toHaveBeenCalled(); }); it('should call callback', async () => { await server.listen(callback); await server.close(); - expect(callback.called).to.be.true; + expect(callback).toHaveBeenCalled(); }); describe('when "start" throws an exception', () => { it('should call callback with a thrown error as an argument', async () => { const error = new Error('random error'); - const callbackSpy = sinon.spy(); - sinon.stub(server, 'createClient').callsFake(async () => null); + const callbackSpy = vi.fn(); + vi.spyOn(server, 'createClient').mockImplementation(async () => null); - sinon.stub(server, 'start').callsFake(() => { + vi.spyOn(server, 'start').mockImplementation(() => { throw error; }); await server.listen(callbackSpy); - expect(callbackSpy.calledWith(error)).to.be.true; + expect(callbackSpy).toHaveBeenCalledWith(error); }); }); }); describe('listen (multiple proto)', () => { - let callback: sinon.SinonSpy; - let bindEventsStub: sinon.SinonStub; + let callback: ReturnType; + let bindEventsStub: ReturnType; beforeEach(() => { - callback = sinon.spy(); - bindEventsStub = sinon - .stub(serverMulti, 'bindEvents') - .callsFake(() => ({}) as any); + callback = vi.fn(); + bindEventsStub = vi + .spyOn(serverMulti, 'bindEvents') + .mockImplementation(() => ({}) as any); }); it('should call "bindEvents"', async () => { await serverMulti.listen(callback); await serverMulti.close(); - expect(bindEventsStub.called).to.be.true; + expect(bindEventsStub).toHaveBeenCalled(); }); it('should call callback', async () => { await serverMulti.listen(callback); await serverMulti.close(); - expect(callback.called).to.be.true; + expect(callback).toHaveBeenCalled(); }); }); describe('bindEvents', () => { beforeEach(() => { - sinon.stub(server, 'loadProto').callsFake(() => ({})); + vi.spyOn(server, 'loadProto').mockImplementation(() => ({})); }); describe('when package does not exist', () => { it('should throw "InvalidGrpcPackageException"', async () => { - sinon.stub(server, 'lookupPackage').callsFake(() => null); + vi.spyOn(server, 'lookupPackage').mockImplementation(() => null); untypedServer.logger = new NoopLogger(); - try { - await server.bindEvents(); - } catch (err) { - expect(err).to.be.instanceOf(InvalidGrpcPackageException); - } + await expect(server.bindEvents()).rejects.toBeInstanceOf( + InvalidGrpcPackageException, + ); }); }); describe('when package exist', () => { @@ -125,32 +121,32 @@ describe('ServerGrpc', () => { service: true, }, ]; - sinon.stub(server, 'lookupPackage').callsFake(() => ({ + vi.spyOn(server, 'lookupPackage').mockImplementation(() => ({ test: { service: true }, test2: { service: true }, })); - sinon.stub(server, 'getServiceNames').callsFake(() => serviceNames); - untypedServer.grpcClient = { addService: sinon.spy() }; + vi.spyOn(server, 'getServiceNames').mockImplementation( + () => serviceNames, + ); + untypedServer.grpcClient = { addService: vi.fn() }; await server.bindEvents(); - expect(untypedServer.grpcClient.addService.calledTwice).to.be.true; + expect(untypedServer.grpcClient.addService).toHaveBeenCalledTimes(2); }); }); }); describe('bindEvents (multiple proto)', () => { beforeEach(() => { - sinon.stub(serverMulti, 'loadProto').callsFake(() => ({})); + vi.spyOn(serverMulti, 'loadProto').mockImplementation(() => ({})); }); describe('when package does not exist', () => { it('should throw "InvalidGrpcPackageException"', async () => { - sinon.stub(serverMulti, 'lookupPackage').callsFake(() => null); + vi.spyOn(serverMulti, 'lookupPackage').mockImplementation(() => null); (serverMulti as any).logger = new NoopLogger(); - try { - await serverMulti.bindEvents(); - } catch (err) { - expect(err).to.be.instanceOf(InvalidGrpcPackageException); - } + await expect(serverMulti.bindEvents()).rejects.toBeInstanceOf( + InvalidGrpcPackageException, + ); }); }); describe('when package exist', () => { @@ -161,18 +157,19 @@ describe('ServerGrpc', () => { service: true, }, ]; - sinon.stub(serverMulti, 'lookupPackage').callsFake(() => ({ + vi.spyOn(serverMulti, 'lookupPackage').mockImplementation(() => ({ test: { service: true }, })); - sinon - .stub(serverMulti, 'getServiceNames') - .callsFake(() => serviceNames); + vi.spyOn(serverMulti, 'getServiceNames').mockImplementation( + () => serviceNames, + ); - (serverMulti as any).grpcClient = { addService: sinon.spy() }; + (serverMulti as any).grpcClient = { addService: vi.fn() }; await serverMulti.bindEvents(); - expect((serverMulti as any).grpcClient.addService.calledTwice).to.be - .true; + expect( + (serverMulti as any).grpcClient.addService, + ).toHaveBeenCalledTimes(2); }); }); }); @@ -194,29 +191,23 @@ describe('ServerGrpc', () => { service: { service: true }, }, ]; - expect(server.getServiceNames(obj)).to.be.eql(expected); + expect(server.getServiceNames(obj)).toEqual(expected); }); }); describe('createService', () => { - const objectToMap = obj => - new Map(Object.keys(obj).map(key => [key, obj[key]]) as any); - it('should call "createServiceMethod"', async () => { const handlers = objectToMap({ test: null, test2: () => ({}), }); - sinon - .stub(server, 'createPattern') - .onFirstCall() - .returns('test') - .onSecondCall() - .returns('test2'); - - const spy = sinon - .stub(server, 'createServiceMethod') - .callsFake(() => ({}) as any); + vi.spyOn(server, 'createPattern') + .mockReturnValueOnce('test') + .mockReturnValueOnce('test2'); + + const spy = vi + .spyOn(server, 'createServiceMethod') + .mockImplementation(() => ({}) as any); untypedServer.messageHandlers = handlers; await server.createService( { @@ -224,7 +215,7 @@ describe('ServerGrpc', () => { }, 'name', ); - expect(spy.calledOnce).to.be.true; + expect(spy).toHaveBeenCalledOnce(); }); describe('when RX streaming', () => { it('should call "createPattern" with proper arguments', async () => { @@ -233,12 +224,13 @@ describe('ServerGrpc', () => { requestStream: true, }, }); - const createPatternStub = sinon - .stub(server, 'createPattern') - .onFirstCall() - .returns('test2'); + const createPatternStub = vi + .spyOn(server, 'createPattern') + .mockReturnValueOnce('test2'); - sinon.stub(server, 'createServiceMethod').callsFake(() => ({}) as any); + vi.spyOn(server, 'createServiceMethod').mockImplementation( + () => ({}) as any, + ); untypedServer.messageHandlers = handlers; await server.createService( { @@ -250,13 +242,11 @@ describe('ServerGrpc', () => { }, 'name', ); - expect( - createPatternStub.calledWith( - 'name', - 'test2', - GrpcMethodStreamingType.RX_STREAMING, - ), - ).to.be.true; + expect(createPatternStub).toHaveBeenCalledWith( + 'name', + 'test2', + GrpcMethodStreamingType.RX_STREAMING, + ); }); }); describe('when pass through streaming', () => { @@ -266,16 +256,15 @@ describe('ServerGrpc', () => { requestStream: true, }, }); - const createPatternStub = sinon - .stub(server, 'createPattern') - .onFirstCall() - .returns('_invalid') - .onSecondCall() - .returns('_invalid') - .onThirdCall() - .returns('test2'); - - sinon.stub(server, 'createServiceMethod').callsFake(() => ({}) as any); + const createPatternStub = vi + .spyOn(server, 'createPattern') + .mockReturnValueOnce('_invalid') + .mockReturnValueOnce('_invalid') + .mockReturnValueOnce('test2'); + + vi.spyOn(server, 'createServiceMethod').mockImplementation( + () => ({}) as any, + ); untypedServer.messageHandlers = handlers; await server.createService( { @@ -287,13 +276,11 @@ describe('ServerGrpc', () => { }, 'name', ); - expect( - createPatternStub.calledWith( - 'name', - 'test2', - GrpcMethodStreamingType.PT_STREAMING, - ), - ).to.be.true; + expect(createPatternStub).toHaveBeenCalledWith( + 'name', + 'test2', + GrpcMethodStreamingType.PT_STREAMING, + ); }); }); }); @@ -306,7 +293,6 @@ describe('ServerGrpc', () => { GrpcMethodStreamingType.NO_STREAMING, ); const handlers = new Map([[testPattern, () => ({})]]); - console.log(handlers.entries()); untypedServer.messageHandlers = handlers; expect( @@ -316,7 +302,7 @@ describe('ServerGrpc', () => { GrpcMethodStreamingType.NO_STREAMING, {}, ), - ).not.to.be.undefined; + ).not.toBeUndefined(); }); it('should return handler when package name specified with service name', () => { const testPattern = server.createPattern( @@ -336,7 +322,7 @@ describe('ServerGrpc', () => { path: '/package.example.test/TestMethod', }, ), - ).not.to.be.undefined; + ).not.toBeUndefined(); }); it('should return undefined when method name is unknown', () => { @@ -357,7 +343,7 @@ describe('ServerGrpc', () => { path: '/package.example.test/TestMethod', }, ), - ).to.be.undefined; + ).toBeUndefined(); }); }); @@ -371,7 +357,7 @@ describe('ServerGrpc', () => { method, GrpcMethodStreamingType.NO_STREAMING, ), - ).to.be.eql( + ).toEqual( JSON.stringify({ service, rpc: method, @@ -384,55 +370,55 @@ describe('ServerGrpc', () => { describe('createServiceMethod', () => { describe('when method is a response stream', () => { it('should call "createStreamServiceMethod"', () => { - const cln = sinon.spy(); - const spy = sinon.spy(server, 'createStreamServiceMethod'); + const cln = vi.fn(); + const spy = vi.spyOn(server, 'createStreamServiceMethod'); server.createServiceMethod( cln, { responseStream: true } as any, GrpcMethodStreamingType.NO_STREAMING, ); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); describe('when method is not a response stream', () => { it('should call "createUnaryServiceMethod"', () => { - const cln = sinon.spy(); - const spy = sinon.spy(server, 'createUnaryServiceMethod'); + const cln = vi.fn(); + const spy = vi.spyOn(server, 'createUnaryServiceMethod'); server.createServiceMethod( cln, { responseStream: false } as any, GrpcMethodStreamingType.NO_STREAMING, ); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); describe('when request is a stream', () => { describe('when stream type is RX_STREAMING', () => { it('should call "createRequestStreamMethod"', () => { - const cln = sinon.spy(); - const spy = sinon.spy(server, 'createRequestStreamMethod'); + const cln = vi.fn(); + const spy = vi.spyOn(server, 'createRequestStreamMethod'); server.createServiceMethod( cln, { requestStream: true } as any, GrpcMethodStreamingType.RX_STREAMING, ); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); describe('when stream type is PT_STREAMING', () => { it('should call "createStreamCallMethod"', () => { - const cln = sinon.spy(); - const spy = sinon.spy(server, 'createStreamCallMethod'); + const cln = vi.fn(); + const spy = vi.spyOn(server, 'createStreamCallMethod'); server.createServiceMethod( cln, { requestStream: true } as any, GrpcMethodStreamingType.PT_STREAMING, ); - expect(spy.called).to.be.true; + expect(spy).toHaveBeenCalled(); }); }); }); @@ -440,45 +426,48 @@ describe('ServerGrpc', () => { describe('createStreamServiceMethod', () => { it('should return function', () => { - const fn = server.createStreamServiceMethod(sinon.spy()); - expect(fn).to.be.a('function'); + const fn = server.createStreamServiceMethod(vi.fn()); + expect(fn).toBeTypeOf('function'); }); describe('on call', () => { it('should call native method', async () => { const call = { - write: sinon.spy(() => true), - end: sinon.spy(), - on: sinon.spy(), - off: sinon.spy(), + write: vi.fn(() => true), + end: vi.fn(), + on: vi.fn(), + off: vi.fn(), }; - const callback = sinon.spy(); - const native = sinon.spy(); + const callback = vi.fn(); + const native = vi.fn(); await server.createStreamServiceMethod(native)(call, callback); - expect(native.called).to.be.true; - expect(call.on.calledWith('cancelled')).to.be.true; - expect(call.off.calledWith('cancelled')).to.be.true; + expect(native).toHaveBeenCalled(); + expect(call.on).toHaveBeenCalledWith('cancelled', expect.any(Function)); + expect(call.off).toHaveBeenCalledWith( + 'cancelled', + expect.any(Function), + ); }); it('should handle error thrown in handler', async () => { const call = { - write: sinon.spy(() => true), - end: sinon.spy(), - on: sinon.spy(), - off: sinon.spy(), - emit: sinon.spy(), + write: vi.fn(() => true), + end: vi.fn(), + on: vi.fn(), + off: vi.fn(), + emit: vi.fn(), }; - const callback = sinon.spy(); + const callback = vi.fn(); const error = new Error('handler threw'); - const native = sinon.spy(() => throwError(() => error)); + const native = vi.fn(() => throwError(() => error)); // implicit assertion that this will never throw when call.emit emits an error event await server.createStreamServiceMethod(native)(call, callback); - expect(native.called).to.be.true; - expect(call.emit.calledWith('error', error)).to.be.ok; - expect(call.end.called).to.be.true; + expect(native).toHaveBeenCalled(); + expect(call.emit).toHaveBeenCalledWith('error', error); + expect(call.end).toHaveBeenCalled(); }); it(`should close the result observable when receiving an 'cancelled' event from the client`, async () => { @@ -487,43 +476,46 @@ describe('ServerGrpc', () => { const written: any[] = []; const call = { - write: sinon.spy((value: any) => { + write: vi.fn((value: any) => { written.push(value); return true; }), - end: sinon.spy(() => written.push('end')), - on: sinon.spy((name, cb) => { + end: vi.fn(() => written.push('end')), + on: vi.fn((name, cb) => { et.addEventListener(name, cb); }), - off: sinon.spy((name, cb) => { + off: vi.fn((name, cb) => { et.removeEventListener(name, cb); }), }; const result$ = new Subject(); const resolvedObservable = Promise.resolve(result$); - const callback = sinon.spy(); - const native = sinon.stub().returns(resolvedObservable); + const callback = vi.fn(); + const native = vi.fn().mockReturnValue(resolvedObservable); const result = server.createStreamServiceMethod(native)(call, callback); await resolvedObservable; result$.next(1); - expect(written).to.deep.equal([1]); + expect(written).toEqual([1]); result$.next(2); - expect(written).to.deep.equal([1, 2]); + expect(written).toEqual([1, 2]); result$.next(3); - expect(written).to.deep.equal([1, 2, 3]); + expect(written).toEqual([1, 2, 3]); cancel(); result$.next(4); - expect(written).to.deep.equal([1, 2, 3, 'end']); - - expect(call.end.called).to.be.true; - expect(call.on.calledWith('cancelled')).to.be.true; - expect(call.on.calledWith('drain')).to.be.true; - expect(call.off.calledWith('cancelled')).to.be.true; - expect(call.off.calledWith('drain')).to.be.true; + expect(written).toEqual([1, 2, 3, 'end']); + + expect(call.end).toHaveBeenCalled(); + expect(call.on).toHaveBeenCalledWith('cancelled', expect.any(Function)); + expect(call.on).toHaveBeenCalledWith('drain', expect.any(Function)); + expect(call.off).toHaveBeenCalledWith( + 'cancelled', + expect.any(Function), + ); + expect(call.off).toHaveBeenCalledWith('drain', expect.any(Function)); await result; }); @@ -532,76 +524,70 @@ describe('ServerGrpc', () => { describe('createUnaryServiceMethod', () => { it('should return observable', () => { - const fn = server.createUnaryServiceMethod(sinon.spy()); - expect(fn).to.be.a('function'); + const fn = server.createUnaryServiceMethod(vi.fn()); + expect(fn).toBeTypeOf('function'); }); describe('on call', () => { it('should call native & callback methods', async () => { - const call = { write: sinon.spy(), end: sinon.spy() }; - const callback = sinon.spy(); - const native = sinon.spy(); + const call = { write: vi.fn(), end: vi.fn() }; + const callback = vi.fn(); + const native = vi.fn(); await server.createUnaryServiceMethod(native)(call, callback); - expect(native.called).to.be.true; - expect(callback.called).to.be.true; + expect(native).toHaveBeenCalled(); + expect(callback).toHaveBeenCalled(); }); it('should await when a promise is return by the native', async () => { - const call = { write: sinon.spy(), end: sinon.spy() }; - const callback = sinon.spy(); - - const original = { native: Function }; - const mock = sinon.mock(original); - - mock - .expects('native') - .once() - .returns( - (() => { - const sub = new ReplaySubject(1); - sub.next(new Promise(resolve => resolve({ foo: 'bar' }))); - return sub.asObservable(); - })(), - ); + const call = { write: vi.fn(), end: vi.fn() }; + const callback = vi.fn(); + + const native = vi.fn().mockReturnValue( + (() => { + const sub = new ReplaySubject(1); + sub.next(new Promise(resolve => resolve({ foo: 'bar' }))); + return sub.asObservable(); + })(), + ); - await server.createUnaryServiceMethod(original.native)(call, callback); - mock.verify(); - expect(callback.calledWith(null, { foo: 'bar' })).to.be.true; + await server.createUnaryServiceMethod(native)(call, callback); + expect(native).toHaveBeenCalledOnce(); + expect(callback).toHaveBeenCalledWith(null, { foo: 'bar' }); }); }); }); describe('createRequestStreamMethod', () => { it('should wrap call into Subject', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); const fn = server.createRequestStreamMethod(handler, false); const call = { on: (event, callback) => callback(), - off: sinon.spy(), - end: sinon.spy(), - write: sinon.spy(), + off: vi.fn(), + end: vi.fn(), + write: vi.fn(), }; - await fn(call as any, sinon.spy()); + await fn(call as any, vi.fn()); - expect(handler.called).to.be.true; + expect(handler).toHaveBeenCalled(); }); it('should wrap call into Subject with metadata', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); const fn = server.createRequestStreamMethod(handler, false); const call = { on: (event, callback) => callback(), - off: sinon.spy(), - end: sinon.spy(), - write: sinon.spy(), + off: vi.fn(), + end: vi.fn(), + write: vi.fn(), metadata: { test: '123', }, }; - await fn(call as any, sinon.spy()); + await fn(call as any, vi.fn()); - expect(handler.called).to.be.true; - expect(handler.args[0][1]).to.eq(call.metadata); + expect(handler).toHaveBeenCalled(); + expect(handler.mock.calls[0][1]).toBe(call.metadata); }); describe('when response is not a stream', () => { @@ -614,15 +600,15 @@ describe('ServerGrpc', () => { callback(); } }, - off: sinon.spy(), - end: sinon.spy(), - write: sinon.spy(() => false), + off: vi.fn(), + end: vi.fn(), + write: vi.fn(() => false), }; - const responseCallback = sinon.spy(); + const responseCallback = vi.fn(); await fn(call as any, responseCallback); - expect(responseCallback.called).to.be.true; + expect(responseCallback).toHaveBeenCalled(); }); it('should handle error thrown in handler', async () => { @@ -635,16 +621,16 @@ describe('ServerGrpc', () => { callback(); } }, - off: sinon.spy(), - end: sinon.spy(), - write: sinon.spy(), + off: vi.fn(), + end: vi.fn(), + write: vi.fn(), }; - const responseCallback = sinon.spy(); + const responseCallback = vi.fn(); await fn(call as any, responseCallback); - expect(responseCallback.calledOnce).to.be.true; - expect(responseCallback.firstCall.args).to.eql([error, null]); + expect(responseCallback).toHaveBeenCalledOnce(); + expect(responseCallback.mock.calls[0]).toEqual([error, null]); }); describe('when response is a stream', () => { @@ -671,7 +657,7 @@ describe('ServerGrpc', () => { }; const call = { - write: sinon.spy(value => { + write: vi.fn(value => { // Simulating a writable stream becoming overwhelmed. if (writeCounter++ < highwaterMark) { // We can write this value to the stream. @@ -680,13 +666,13 @@ describe('ServerGrpc', () => { // But as soon as we pass the highwater mark, we can't write anymore. return writeCounter < highwaterMark; }), - end: sinon.spy(() => { + end: vi.fn(() => { written.push('end'); }), - emit: sinon.spy(), - request: sinon.spy(), - metadata: sinon.spy(), - sendMetadata: sinon.spy(), + emit: vi.fn(), + request: vi.fn(), + metadata: vi.fn(), + sendMetadata: vi.fn(), on: (name, cb) => { emitter.addEventListener(name, cb); }, @@ -699,7 +685,7 @@ describe('ServerGrpc', () => { }, }; - const callback = sinon.spy(); + const callback = vi.fn(); const subject = new Subject(); const handlerResult = Promise.resolve(subject); @@ -726,15 +712,15 @@ describe('ServerGrpc', () => { callback(); } }, - off: sinon.spy(), - end: sinon.spy(), - write: sinon.spy(() => true), + off: vi.fn(), + end: vi.fn(), + write: vi.fn(() => true), }; await fn(call as any, null!); - expect(call.write.called).to.be.true; - expect(call.end.called).to.be.true; + expect(call.write).toHaveBeenCalled(); + expect(call.end).toHaveBeenCalled(); }); it('should drain all values emitted from the observable while waiting for the drain event from the call', async () => { @@ -742,20 +728,20 @@ describe('ServerGrpc', () => { subject.next('a'); subject.next('b'); - expect(written).to.deep.equal(['a', 'b']); + expect(written).toEqual(['a', 'b']); subject.next('c'); // can't be written yet. - expect(written).to.deep.equal(['a', 'b']); + expect(written).toEqual(['a', 'b']); call.fire.drain(); subject.next('d'); - expect(written).to.deep.equal(['a', 'b', 'c', 'd']); + expect(written).toEqual(['a', 'b', 'c', 'd']); subject.next('e'); // can't be written yet. - expect(written).to.deep.equal(['a', 'b', 'c', 'd']); + expect(written).toEqual(['a', 'b', 'c', 'd']); call.fire.drain(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd', 'e']); + expect(written).toEqual(['a', 'b', 'c', 'd', 'e']); subject.next('f'); - expect(written).to.deep.equal(['a', 'b', 'c', 'd', 'e', 'f']); + expect(written).toEqual(['a', 'b', 'c', 'd', 'e', 'f']); subject.complete(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd', 'e', 'f', 'end']); + expect(written).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'end']); return result; }); @@ -771,13 +757,13 @@ describe('ServerGrpc', () => { subject.next('c'); subject.next('d'); subject.next('e'); - expect(written).to.deep.equal(['a', 'b']); + expect(written).toEqual(['a', 'b']); call.fire.drain(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd']); + expect(written).toEqual(['a', 'b', 'c', 'd']); call.fire.drain(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd', 'e']); + expect(written).toEqual(['a', 'b', 'c', 'd', 'e']); subject.complete(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd', 'e', 'end']); + expect(written).toEqual(['a', 'b', 'c', 'd', 'e', 'end']); return result; }, @@ -792,11 +778,11 @@ describe('ServerGrpc', () => { subject.next('d'); subject.next('e'); subject.complete(); - expect(written).to.deep.equal(['a', 'b']); + expect(written).toEqual(['a', 'b']); call.fire.drain(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd']); + expect(written).toEqual(['a', 'b', 'c', 'd']); call.fire.drain(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd', 'e', 'end']); + expect(written).toEqual(['a', 'b', 'c', 'd', 'e', 'end']); return result; }); @@ -804,10 +790,10 @@ describe('ServerGrpc', () => { it('should end the subscription to the source if the call is cancelled', async () => { const { call, subject, result } = await createCall(); - expect(subject.observed).to.be.true; + expect(subject.observed).toBe(true); call.fire.cancel(); - expect(subject.observed).to.be.false; - expect(call.end.called).to.be.true; + expect(subject.observed).toBe(false); + expect(call.end).toHaveBeenCalled(); return result; }); @@ -821,16 +807,16 @@ describe('ServerGrpc', () => { subject.next('d'); subject.next('e'); subject.error(error); - expect(written).to.deep.equal(['a', 'b']); + expect(written).toEqual(['a', 'b']); call.fire.drain(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd']); + expect(written).toEqual(['a', 'b', 'c', 'd']); call.fire.drain(); - expect(written).to.deep.equal(['a', 'b', 'c', 'd', 'e', 'end']); + expect(written).toEqual(['a', 'b', 'c', 'd', 'e', 'end']); try { await result; } catch (err) { - expect(err).to.equal(error); + expect(err).toBe(error); } }); }); @@ -839,30 +825,26 @@ describe('ServerGrpc', () => { describe('createStreamCallMethod', () => { it('should pass through to "methodHandler"', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); const fn = server.createStreamCallMethod(handler, false); const args = [1, 2, 3]; - await fn(args as any, sinon.spy()); + await fn(args as any, vi.fn()); - expect(handler.calledWith(args)).to.be.true; + expect(handler).toHaveBeenCalledWith(args, expect.any(Function)); }); }); describe('loadProto', () => { describe('when proto is invalid', () => { it('should throw InvalidProtoDefinitionException', () => { - const getPackageDefinitionStub = sinon.stub( - grpcHelpers, - 'getGrpcPackageDefinition' as any, - ); - getPackageDefinitionStub.callsFake(() => { - throw new Error(); + const invalidServer = new ServerGrpc({ + protoPath: '/nonexistent/invalid.proto', + package: 'test', }); - untypedServer.logger = new NoopLogger(); - expect(() => server.loadProto()).to.throws( + (invalidServer as any).logger = new NoopLogger(); + expect(() => invalidServer.loadProto()).toThrow( InvalidProtoDefinitionException, ); - getPackageDefinitionStub.restore(); }); }); }); @@ -870,50 +852,48 @@ describe('ServerGrpc', () => { describe('close', () => { it('should call "forceShutdown" by default', async () => { const grpcClient = { - forceShutdown: sinon.spy(), - tryShutdown: sinon.stub().yields(), + forceShutdown: vi.fn(), + tryShutdown: vi.fn(cb => cb()), }; untypedServer.grpcClient = grpcClient; await server.close(); - expect(grpcClient.forceShutdown.called).to.be.true; - expect(grpcClient.tryShutdown.called).to.be.false; + expect(grpcClient.forceShutdown).toHaveBeenCalled(); + expect(grpcClient.tryShutdown).not.toHaveBeenCalled(); }); it('should call "forceShutdown" when "gracefulShutdown" is false', async () => { const grpcClient = { - forceShutdown: sinon.spy(), - tryShutdown: sinon.stub().yields(), + forceShutdown: vi.fn(), + tryShutdown: vi.fn(cb => cb()), }; untypedServer.grpcClient = grpcClient; untypedServer.options.gracefulShutdown = false; await server.close(); - expect(grpcClient.forceShutdown.called).to.be.true; - expect(grpcClient.tryShutdown.called).to.be.false; + expect(grpcClient.forceShutdown).toHaveBeenCalled(); + expect(grpcClient.tryShutdown).not.toHaveBeenCalled(); }); it('should call "tryShutdown" when "gracefulShutdown" is true', async () => { const grpcClient = { - forceShutdown: sinon.spy(), - tryShutdown: sinon.stub().yields(), + forceShutdown: vi.fn(), + tryShutdown: vi.fn(cb => cb()), }; untypedServer.grpcClient = grpcClient; untypedServer.options.gracefulShutdown = true; await server.close(); - expect(grpcClient.forceShutdown.called).to.be.false; - expect(grpcClient.tryShutdown.called).to.be.true; + expect(grpcClient.forceShutdown).not.toHaveBeenCalled(); + expect(grpcClient.tryShutdown).toHaveBeenCalled(); }); }); describe('deserialize', () => { it(`should return parsed json`, () => { const obj = { test: 'test' }; - expect(server.deserialize(obj)).to.deep.equal( - JSON.parse(JSON.stringify(obj)), - ); + expect(server.deserialize(obj)).toEqual(JSON.parse(JSON.stringify(obj))); }); it(`should not parse argument if it is not an object`, () => { const content = 'test'; - expect(server.deserialize(content)).to.equal(content); + expect(server.deserialize(content)).toBe(content); }); }); @@ -938,12 +918,9 @@ describe('ServerGrpc', () => { }, }; const svcs = server.getServiceNames(grpcPkg); - expect(svcs.length).to.be.equal( - 2, - 'Amount of services collected from namespace should be equal 2', - ); - expect(svcs[0].name).to.be.equal('A.C.E'); - expect(svcs[1].name).to.be.equal('B.D'); + expect(svcs.length).toBe(2); + expect(svcs[0].name).toBe('A.C.E'); + expect(svcs[1].name).toBe('B.D'); }); it('should parse single level proto package tree"', () => { const grpcPkg = { @@ -959,12 +936,9 @@ describe('ServerGrpc', () => { }, }; const services = server.getServiceNames(grpcPkg); - expect(services.length).to.be.equal( - 2, - 'Amount of services collected from namespace should be equal 2', - ); - expect(services[0].name).to.be.equal('A'); - expect(services[1].name).to.be.equal('B'); + expect(services.length).toBe(2); + expect(services[0].name).toBe('A'); + expect(services[1].name).toBe('B'); }); }); @@ -973,16 +947,18 @@ describe('ServerGrpc', () => { pattern = { test: 'test pattern' }; it(`should add handler`, () => { - sinon.stub(server as any, 'messageHandlers').value({ set() {} }); + vi.spyOn(server as any, 'messageHandlers', 'get').mockReturnValue({ + set() {}, + }); - const messageHandlersSetSpy = sinon.spy( + const messageHandlersSetSpy = vi.spyOn( untypedServer.messageHandlers, 'set', ); server.addHandler(pattern, callback as any); - expect(messageHandlersSetSpy.called).to.be.true; - expect(messageHandlersSetSpy.getCall(0).args[0]).to.be.equal( + expect(messageHandlersSetSpy).toHaveBeenCalled(); + expect(messageHandlersSetSpy.mock.calls[0][0]).toBe( JSON.stringify(pattern), ); }); diff --git a/packages/microservices/test/server/server-kafka.spec.ts b/packages/microservices/test/server/server-kafka.spec.ts index 20cda17f1be..073f750acad 100644 --- a/packages/microservices/test/server/server-kafka.spec.ts +++ b/packages/microservices/test/server/server-kafka.spec.ts @@ -1,15 +1,13 @@ import { Logger } from '@nestjs/common'; -import { AssertionError, expect } from 'chai'; -import * as sinon from 'sinon'; -import { NO_MESSAGE_HANDLER } from '../../constants'; -import { KafkaContext } from '../../ctx-host'; -import { KafkaHeaders } from '../../enums'; +import { NO_MESSAGE_HANDLER } from '../../constants.js'; +import { KafkaContext } from '../../ctx-host/index.js'; +import { KafkaHeaders } from '../../enums/index.js'; import { EachMessagePayload, KafkaMessage, -} from '../../external/kafka.interface'; -import { ServerKafka } from '../../server'; -import { objectToMap } from './utils/object-to-map'; +} from '../../external/kafka.interface.js'; +import { ServerKafka } from '../../server/index.js'; +import { objectToMap } from './utils/object-to-map.js'; class NoopLogger extends Logger { log(message: any, context?: string): void {} @@ -84,29 +82,29 @@ describe('ServerKafka', () => { let server: ServerKafka; let untypedServer: any; - let callback: sinon.SinonSpy; - let bindEventsStub: sinon.SinonStub; - let connect: sinon.SinonSpy; - let subscribe: sinon.SinonSpy; - let run: sinon.SinonSpy; - let send: sinon.SinonSpy; - let on: sinon.SinonSpy; - let consumerStub: sinon.SinonStub; - let producerStub: sinon.SinonStub; + let callback: ReturnType; + let bindEventsStub: ReturnType; + let connect: ReturnType; + let subscribe: ReturnType; + let run: ReturnType; + let send: ReturnType; + let on: ReturnType; + let consumerStub: ReturnType; + let producerStub: ReturnType; let client: any; beforeEach(() => { server = new ServerKafka({}); untypedServer = server as any; - callback = sinon.spy(); - connect = sinon.spy(); - subscribe = sinon.spy(); - run = sinon.spy(); - send = sinon.spy(); - on = sinon.spy(); + callback = vi.fn(); + connect = vi.fn(); + subscribe = vi.fn(); + run = vi.fn(); + send = vi.fn(); + on = vi.fn(); - consumerStub = sinon.stub(server as any, 'consumer').callsFake(() => { + consumerStub = vi.fn(() => { return { connect, subscribe, @@ -132,7 +130,7 @@ describe('ServerKafka', () => { }, }; }); - producerStub = sinon.stub(server as any, 'producer').callsFake(() => { + producerStub = vi.fn(() => { return { connect, send, @@ -150,41 +148,41 @@ describe('ServerKafka', () => { consumer: consumerStub, producer: producerStub, }; - sinon.stub(server, 'createClient').callsFake(() => client); + vi.spyOn(server, 'createClient').mockImplementation(async () => client); untypedServer = server as any; }); describe('listen', () => { it('should call "bindEvents"', async () => { - bindEventsStub = sinon - .stub(server, 'bindEvents') - .callsFake(() => ({}) as any); + bindEventsStub = vi + .spyOn(server, 'bindEvents') + .mockImplementation(() => ({}) as any); await server.listen(err => console.log(err)); - expect(bindEventsStub.called).to.be.true; + expect(bindEventsStub).toHaveBeenCalled(); }); it('should call callback', async () => { await server.listen(callback); - expect(callback.called).to.be.true; + expect(callback).toHaveBeenCalled(); }); describe('when "start" throws an exception', () => { it('should call callback with a thrown error as an argument', async () => { const error = new Error('random error'); - const callbackSpy = sinon.spy(); - sinon.stub(server, 'start').callsFake(() => { + const callbackSpy = vi.fn(); + vi.spyOn(server, 'start').mockImplementation(() => { throw error; }); await server.listen(callbackSpy); - expect(callbackSpy.calledWith(error)).to.be.true; + expect(callbackSpy).toHaveBeenCalledWith(error); }); }); }); describe('close', () => { - const consumer = { disconnect: sinon.spy() }; - const producer = { disconnect: sinon.spy() }; + const consumer = { disconnect: vi.fn() }; + const producer = { disconnect: vi.fn() }; beforeEach(() => { untypedServer.consumer = consumer; untypedServer.producer = producer; @@ -192,11 +190,11 @@ describe('ServerKafka', () => { it('should close server', async () => { await server.close(); - expect(consumer.disconnect.calledOnce).to.be.true; - expect(producer.disconnect.calledOnce).to.be.true; - expect(untypedServer.consumer).to.be.null; - expect(untypedServer.producer).to.be.null; - expect(untypedServer.client).to.be.null; + expect(consumer.disconnect).toHaveBeenCalledOnce(); + expect(producer.disconnect).toHaveBeenCalledOnce(); + expect(untypedServer.consumer).toBeNull(); + expect(untypedServer.producer).toBeNull(); + expect(untypedServer.client).toBeNull(); }); }); @@ -205,31 +203,29 @@ describe('ServerKafka', () => { untypedServer.logger = new NoopLogger(); await server.listen(callback); await server.bindEvents(untypedServer.consumer); - expect(subscribe.called).to.be.false; - expect(run.called).to.be.true; - expect(connect.called).to.be.true; + expect(subscribe).not.toHaveBeenCalled(); + expect(run).toHaveBeenCalled(); + expect(connect).toHaveBeenCalled(); }); it('should call subscribe and run on consumer when there are messageHandlers', async () => { untypedServer.logger = new NoopLogger(); await server.listen(callback); const pattern = 'test'; - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [pattern]: handler, }); await server.bindEvents(untypedServer.consumer); - expect(subscribe.called).to.be.true; - expect( - subscribe.calledWith({ - topics: [pattern], - }), - ).to.be.true; + expect(subscribe).toHaveBeenCalled(); + expect(subscribe).toHaveBeenCalledWith({ + topics: [pattern], + }); - expect(run.called).to.be.true; - expect(connect.called).to.be.true; + expect(run).toHaveBeenCalled(); + expect(connect).toHaveBeenCalled(); }); it('should call subscribe with options and run on consumer when there are messageHandlers', async () => { untypedServer.logger = new NoopLogger(); @@ -238,23 +234,21 @@ describe('ServerKafka', () => { await server.listen(callback); const pattern = 'test'; - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [pattern]: handler, }); await server.bindEvents(untypedServer.consumer); - expect(subscribe.called).to.be.true; - expect( - subscribe.calledWith({ - topics: [pattern], - fromBeginning: true, - }), - ).to.be.true; + expect(subscribe).toHaveBeenCalled(); + expect(subscribe).toHaveBeenCalledWith({ + topics: [pattern], + fromBeginning: true, + }); - expect(run.called).to.be.true; - expect(connect.called).to.be.true; + expect(run).toHaveBeenCalled(); + expect(connect).toHaveBeenCalled(); }); it('should pass run options with partitionsConsumedConcurrently to consumer.run()', async () => { untypedServer.logger = new NoopLogger(); @@ -264,11 +258,13 @@ describe('ServerKafka', () => { await server.listen(callback); await server.bindEvents(untypedServer.consumer); - expect(run.called).to.be.true; - expect(run.firstCall.args[0]).to.include({ - partitionsConsumedConcurrently: 5, - }); - expect(run.firstCall.args[0]).to.have.property('eachMessage'); + expect(run).toHaveBeenCalled(); + expect(run.mock.calls[0][0]).toEqual( + expect.objectContaining({ + partitionsConsumedConcurrently: 5, + }), + ); + expect(run.mock.calls[0][0]).toHaveProperty('eachMessage'); }); it('should pass multiple run options to consumer.run()', async () => { untypedServer.logger = new NoopLogger(); @@ -280,35 +276,37 @@ describe('ServerKafka', () => { await server.listen(callback); await server.bindEvents(untypedServer.consumer); - expect(run.called).to.be.true; - const callArg = run.firstCall.args[0]; - expect(callArg).to.include({ - partitionsConsumedConcurrently: 3, - autoCommit: false, - autoCommitInterval: 5000, - }); - expect(callArg).to.have.property('eachMessage'); + expect(run).toHaveBeenCalled(); + const callArg = run.mock.calls[0][0]; + expect(callArg).toEqual( + expect.objectContaining({ + partitionsConsumedConcurrently: 3, + autoCommit: false, + autoCommitInterval: 5000, + }), + ); + expect(callArg).toHaveProperty('eachMessage'); }); }); describe('getMessageHandler', () => { it(`should return function`, () => { - expect(typeof server.getMessageHandler()).to.be.eql('function'); + expect(typeof server.getMessageHandler()).toEqual('function'); }); describe('handler', () => { it('should call "handleMessage"', async () => { - const handleMessageStub = sinon - .stub(server, 'handleMessage') - .callsFake(() => null!); + const handleMessageStub = vi + .spyOn(server, 'handleMessage') + .mockImplementation(() => null!); await server.getMessageHandler()(null!); - expect(handleMessageStub.called).to.be.true; + expect(handleMessageStub).toHaveBeenCalled(); }); }); }); describe('getPublisher', () => { const context = new KafkaContext([] as any); - let sendMessageStub: sinon.SinonStub; + let sendMessageStub: ReturnType; let publisher; beforeEach(() => { @@ -318,14 +316,14 @@ describe('ServerKafka', () => { correlationId, context, ); - sendMessageStub = sinon - .stub(server, 'sendMessage') - .callsFake(async () => []); + sendMessageStub = vi + .spyOn(server, 'sendMessage') + .mockImplementation(async () => []); }); it(`should return function`, () => { expect( typeof server.getPublisher(null!, null!, correlationId, context), - ).to.be.eql('function'); + ).toEqual('function'); }); it(`should call "publish" with expected arguments`, () => { const data = { @@ -334,125 +332,122 @@ describe('ServerKafka', () => { }; publisher(data); - expect( - sendMessageStub.calledWith( - data, - replyTopic, - replyPartition, - correlationId, - ), - ).to.be.true; + expect(sendMessageStub).toHaveBeenCalledWith( + data, + replyTopic, + replyPartition, + correlationId, + context, + ); }); }); describe('handleMessage', () => { - let getPublisherSpy: sinon.SinonSpy; + let getPublisherSpy: ReturnType; beforeEach(() => { - sinon.stub(server, 'sendMessage').callsFake(async () => []); - getPublisherSpy = sinon.spy(); + vi.spyOn(server, 'sendMessage').mockImplementation(async () => []); + getPublisherSpy = vi.fn(); - sinon.stub(server, 'getPublisher').callsFake(() => getPublisherSpy); + vi.spyOn(server, 'getPublisher').mockImplementation( + () => getPublisherSpy, + ); }); it('should call "handleEvent" if correlation identifier is not present', async () => { - const handleEventSpy = sinon.spy(server, 'handleEvent'); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); await server.handleMessage(eventPayload); - expect(handleEventSpy.called).to.be.true; + expect(handleEventSpy).toHaveBeenCalled(); }); it('should call "handleEvent" if correlation identifier is present but the reply topic is not present', async () => { - const handleEventSpy = sinon.spy(server, 'handleEvent'); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); await server.handleMessage(eventWithCorrelationIdPayload); - expect(handleEventSpy.called).to.be.true; + expect(handleEventSpy).toHaveBeenCalled(); }); it('should call event handler when "handleEvent" is called', async () => { - const messageHandler = sinon.mock(); + const messageHandler = vi.fn(); const context = { test: true } as any; const messageData = 'some data'; - sinon.stub(server, 'getHandlerByPattern').callsFake(() => messageHandler); + vi.spyOn(server, 'getHandlerByPattern').mockImplementation( + () => messageHandler, + ); await server.handleEvent( topic, { data: messageData, pattern: topic }, context, ); - expect(messageHandler.calledWith(messageData, context)).to.be.true; + expect(messageHandler).toHaveBeenCalledWith(messageData, context); }); it('should not catch error thrown by event handler as part of "handleEvent"', async () => { const error = new Error('handler error'); - const messageHandler = sinon.mock().throwsException(error); - sinon.stub(server, 'getHandlerByPattern').callsFake(() => messageHandler); + const messageHandler = vi.fn().mockImplementation(() => { + throw error; + }); + vi.spyOn(server, 'getHandlerByPattern').mockImplementation( + () => messageHandler, + ); - try { - await server.handleEvent( + await expect( + server.handleEvent( topic, { data: 'some data', pattern: topic }, {} as any, - ); - - // code should not be executed - expect(true).to.be.false; - } catch (e) { - if (e instanceof AssertionError) { - throw e; - } - expect(e).to.be.eq(error); - } + ), + ).rejects.toBe(error); }); it('should call "handleEvent" if correlation identifier and reply topic are present but the handler is of type eventHandler', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); (handler as any).isEventHandler = true; untypedServer.messageHandlers = objectToMap({ [topic]: handler, }); - const handleEventSpy = sinon.spy(server, 'handleEvent'); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); await server.handleMessage(payload); - expect(handleEventSpy.called).to.be.true; + expect(handleEventSpy).toHaveBeenCalled(); }); it('should NOT call "handleEvent" if correlation identifier and reply topic are present but the handler is not of type eventHandler', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); (handler as any).isEventHandler = false; untypedServer.messageHandlers = objectToMap({ [topic]: handler, }); - const handleEventSpy = sinon.spy(server, 'handleEvent'); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); await server.handleMessage(payload); - expect(handleEventSpy.called).to.be.false; + expect(handleEventSpy).not.toHaveBeenCalled(); }); it(`should publish NO_MESSAGE_HANDLER if pattern not exists in messageHandlers object`, async () => { await server.handleMessage(payload); - expect( - getPublisherSpy.calledWith({ - id: payload.message.headers![KafkaHeaders.CORRELATION_ID]!.toString(), - err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + expect(getPublisherSpy).toHaveBeenCalledWith({ + id: payload.message.headers![KafkaHeaders.CORRELATION_ID]!.toString(), + err: NO_MESSAGE_HANDLER, + }); }); it(`should call handler with expected arguments`, async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [topic]: handler, }); await server.handleMessage(payload); - expect(handler.called).to.be.true; + expect(handler).toHaveBeenCalled(); }); }); describe('sendMessage', () => { const context = new KafkaContext([] as any); - let sendSpy: sinon.SinonSpy; + let sendSpy: ReturnType; beforeEach(() => { - sendSpy = sinon.stub().callsFake(() => Promise.resolve()); - sinon.stub(server as any, 'producer').value({ + sendSpy = vi.fn().mockImplementation(() => Promise.resolve()); + vi.spyOn(server as any, 'producer', 'get').mockReturnValue({ send: sendSpy, }); }); @@ -469,20 +464,18 @@ describe('ServerKafka', () => { context, ); - expect( - sendSpy.calledWith({ - topic: replyTopic, - messages: [ - { - partition: parseFloat(replyPartition), - value: messageValue, - headers: { - [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), - }, + expect(sendSpy).toHaveBeenCalledWith({ + topic: replyTopic, + messages: [ + { + partition: parseFloat(replyPartition), + value: messageValue, + headers: { + [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), }, - ], - }), - ).to.be.true; + }, + ], + }); }); it('should send message without reply partition', async () => { await server.sendMessage( @@ -496,19 +489,17 @@ describe('ServerKafka', () => { context, ); - expect( - sendSpy.calledWith({ - topic: replyTopic, - messages: [ - { - value: messageValue, - headers: { - [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), - }, + expect(sendSpy).toHaveBeenCalledWith({ + topic: replyTopic, + messages: [ + { + value: messageValue, + headers: { + [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), }, - ], - }), - ).to.be.true; + }, + ], + }); }); it('should send error message', async () => { await server.sendMessage( @@ -522,21 +513,19 @@ describe('ServerKafka', () => { context, ); - expect( - sendSpy.calledWith({ - topic: replyTopic, - messages: [ - { - value: null, - partition: parseFloat(replyPartition), - headers: { - [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), - [KafkaHeaders.NEST_ERR]: Buffer.from(NO_MESSAGE_HANDLER), - }, + expect(sendSpy).toHaveBeenCalledWith({ + topic: replyTopic, + messages: [ + { + value: null, + partition: parseFloat(replyPartition), + headers: { + [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), + [KafkaHeaders.NEST_ERR]: Buffer.from(NO_MESSAGE_HANDLER), }, - ], - }), - ).to.be.true; + }, + ], + }); }); it('should send `isDisposed` message', async () => { await server.sendMessage( @@ -550,27 +539,25 @@ describe('ServerKafka', () => { context, ); - expect( - sendSpy.calledWith({ - topic: replyTopic, - messages: [ - { - value: null, - partition: parseFloat(replyPartition), - headers: { - [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), - [KafkaHeaders.NEST_IS_DISPOSED]: Buffer.alloc(1), - }, + expect(sendSpy).toHaveBeenCalledWith({ + topic: replyTopic, + messages: [ + { + value: null, + partition: parseFloat(replyPartition), + headers: { + [KafkaHeaders.CORRELATION_ID]: Buffer.from(correlationId), + [KafkaHeaders.NEST_IS_DISPOSED]: Buffer.alloc(1), }, - ], - }), - ).to.be.true; + }, + ], + }); }); }); describe('createClient', () => { - it('should accept a custom logCreator in client options', () => { - const logCreatorSpy = sinon.spy(() => 'test'); + it('should accept a custom logCreator in client options', async () => { + const logCreatorSpy = vi.fn(() => 'test'); const logCreator = () => logCreatorSpy; server = new ServerKafka({ @@ -580,11 +567,12 @@ describe('ServerKafka', () => { }, }); - const logger = server.createClient().logger(); + const kafkaClient = await server.createClient(); + const logger = kafkaClient.logger(); logger.info({ namespace: '', level: 1, log: 'test' }); - expect(logCreatorSpy.called).to.be.true; + expect(logCreatorSpy).toHaveBeenCalled(); }); }); }); diff --git a/packages/microservices/test/server/server-mqtt.spec.ts b/packages/microservices/test/server/server-mqtt.spec.ts index 036c0d3f8c9..6b6456a6de0 100644 --- a/packages/microservices/test/server/server-mqtt.spec.ts +++ b/packages/microservices/test/server/server-mqtt.spec.ts @@ -1,10 +1,8 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { NO_MESSAGE_HANDLER } from '../../constants'; -import { MqttContext } from '../../ctx-host'; -import { BaseRpcContext } from '../../ctx-host/base-rpc.context'; -import { ServerMqtt } from '../../server/server-mqtt'; -import { objectToMap } from './utils/object-to-map'; +import { NO_MESSAGE_HANDLER } from '../../constants.js'; +import { BaseRpcContext } from '../../ctx-host/base-rpc.context.js'; +import { MqttContext } from '../../ctx-host/index.js'; +import { ServerMqtt } from '../../server/server-mqtt.js'; +import { objectToMap } from './utils/object-to-map.js'; describe('ServerMqtt', () => { let server: ServerMqtt; @@ -15,70 +13,72 @@ describe('ServerMqtt', () => { untypedServer = server as any; }); describe('listen', () => { - let onSpy: sinon.SinonSpy; + let onSpy: ReturnType; let client: any; - let callbackSpy: sinon.SinonSpy; + let callbackSpy: ReturnType; beforeEach(() => { - onSpy = sinon.spy(); + onSpy = vi.fn(); client = { on: onSpy, }; - sinon.stub(server, 'createMqttClient').callsFake(() => client); - callbackSpy = sinon.spy(); + vi.spyOn(server, 'createMqttClient').mockImplementation(() => client); + callbackSpy = vi.fn(); }); it('should bind "error" event to handler', async () => { await server.listen(callbackSpy); - expect(onSpy.getCall(0).args[0]).to.be.equal('error'); + expect(onSpy.mock.calls[0][0]).toBe('error'); }); it('should bind "reconnect" event to handler', async () => { await server.listen(callbackSpy); - expect(onSpy.getCall(1).args[0]).to.be.equal('reconnect'); + expect(onSpy.mock.calls[1][0]).toBe('reconnect'); }); it('should bind "disconnect" event to handler', async () => { await server.listen(callbackSpy); - expect(onSpy.getCall(2).args[0]).to.be.equal('disconnect'); + expect(onSpy.mock.calls[2][0]).toBe('disconnect'); }); it('should bind "close" event to handler', async () => { await server.listen(callbackSpy); - expect(onSpy.getCall(3).args[0]).to.be.equal('close'); + expect(onSpy.mock.calls[3][0]).toBe('close'); }); it('should bind "connect" event to handler', async () => { await server.listen(callbackSpy); - expect(onSpy.getCall(4).args[0]).to.be.equal('connect'); + expect(onSpy.mock.calls[4][0]).toBe('connect'); }); it('should bind "message" event to handler', async () => { await server.listen(callbackSpy); - expect(onSpy.getCall(5).args[0]).to.be.equal('message'); + expect(onSpy.mock.calls[5][0]).toBe('message'); }); describe('when "start" throws an exception', () => { it('should call callback with a thrown error as an argument', async () => { const error = new Error('random error'); - sinon.stub(server, 'start').callsFake(() => { + vi.spyOn(server, 'start').mockImplementation(() => { throw error; }); await server.listen(callbackSpy); - expect(callbackSpy.calledWith(error)).to.be.true; + expect(callbackSpy).toHaveBeenCalledWith(error); }); }); }); describe('close', () => { - const mqttClient = { end: sinon.spy() }; + const mqttClient = { end: vi.fn() }; beforeEach(() => { untypedServer.mqttClient = mqttClient; }); it('should end mqttClient', () => { server.close(); - expect(mqttClient.end.called).to.be.true; + expect(mqttClient.end).toHaveBeenCalled(); }); }); describe('bindEvents', () => { - let onSpy: sinon.SinonSpy, subscribeSpy: sinon.SinonSpy, mqttClient; + let onSpy: ReturnType, + subscribeSpy: ReturnType, + mqttClient; beforeEach(() => { - onSpy = sinon.spy(); - subscribeSpy = sinon.spy(); + onSpy = vi.fn(); + subscribeSpy = vi.fn(); mqttClient = { on: onSpy, subscribe: subscribeSpy, @@ -86,37 +86,37 @@ describe('ServerMqtt', () => { }); it('should subscribe to each pattern', () => { const pattern = 'test'; - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [pattern]: handler, }); server.bindEvents(mqttClient); - expect(subscribeSpy.calledWith(pattern)).to.be.true; + expect(subscribeSpy).toHaveBeenCalledWith(pattern, undefined); }); describe('per-handler QoS via extras.qos', () => { it('should use extras.qos=2 when handler specifies qos 2', () => { const pattern = 'alerts/critical'; - const handler = Object.assign(sinon.spy(), { extras: { qos: 2 } }); + const handler = Object.assign(vi.fn(), { extras: { qos: 2 } }); untypedServer.messageHandlers = objectToMap({ [pattern]: handler, }); server.bindEvents(mqttClient); - expect(subscribeSpy.calledOnce).to.be.true; - expect(subscribeSpy.firstCall.args[0]).to.equal(pattern); - expect(subscribeSpy.firstCall.args[1]).to.deep.equal({ qos: 2 }); + expect(subscribeSpy).toHaveBeenCalledOnce(); + expect(subscribeSpy.mock.calls[0][0]).toBe(pattern); + expect(subscribeSpy.mock.calls[0][1]).toEqual({ qos: 2 }); }); it('should use extras.qos=0 when handler specifies qos 0', () => { const pattern = 'telemetry/data'; - const handler = Object.assign(sinon.spy(), { extras: { qos: 0 } }); + const handler = Object.assign(vi.fn(), { extras: { qos: 0 } }); untypedServer.messageHandlers = objectToMap({ [pattern]: handler, }); server.bindEvents(mqttClient); - expect(subscribeSpy.calledOnce).to.be.true; - expect(subscribeSpy.firstCall.args[0]).to.equal(pattern); - expect(subscribeSpy.firstCall.args[1]).to.deep.equal({ qos: 0 }); + expect(subscribeSpy).toHaveBeenCalledOnce(); + expect(subscribeSpy.mock.calls[0][0]).toBe(pattern); + expect(subscribeSpy.mock.calls[0][1]).toEqual({ qos: 0 }); }); it('should use global subscribeOptions when extras.qos is undefined', () => { @@ -126,14 +126,14 @@ describe('ServerMqtt', () => { }); const untypedServerWithOptions = serverWithOptions as any; const pattern = 'events/general'; - const handler = Object.assign(sinon.spy(), { extras: {} }); + const handler = Object.assign(vi.fn(), { extras: {} }); untypedServerWithOptions.messageHandlers = objectToMap({ [pattern]: handler, }); serverWithOptions.bindEvents(mqttClient); - expect(subscribeSpy.calledOnce).to.be.true; - expect(subscribeSpy.firstCall.args[0]).to.equal(pattern); - expect(subscribeSpy.firstCall.args[1]).to.deep.equal({ + expect(subscribeSpy).toHaveBeenCalledOnce(); + expect(subscribeSpy.mock.calls[0][0]).toBe(pattern); + expect(subscribeSpy.mock.calls[0][1]).toEqual({ qos: globalQos, }); }); @@ -144,13 +144,13 @@ describe('ServerMqtt', () => { }); const untypedServerWithOptions = serverWithOptions as any; const pattern = 'commands/run'; - const handler = Object.assign(sinon.spy(), { extras: { qos: 2 } }); + const handler = Object.assign(vi.fn(), { extras: { qos: 2 } }); untypedServerWithOptions.messageHandlers = objectToMap({ [pattern]: handler, }); serverWithOptions.bindEvents(mqttClient); - expect(subscribeSpy.calledOnce).to.be.true; - expect(subscribeSpy.firstCall.args[1]).to.deep.equal({ + expect(subscribeSpy).toHaveBeenCalledOnce(); + expect(subscribeSpy.mock.calls[0][1]).toEqual({ qos: 2, nl: true, rap: false, @@ -163,9 +163,9 @@ describe('ServerMqtt', () => { }); const untypedServerWithOptions = serverWithOptions as any; - const handler0 = Object.assign(sinon.spy(), { extras: { qos: 0 } }); - const handler1 = Object.assign(sinon.spy(), { extras: {} }); - const handler2 = Object.assign(sinon.spy(), { extras: { qos: 2 } }); + const handler0 = Object.assign(vi.fn(), { extras: { qos: 0 } }); + const handler1 = Object.assign(vi.fn(), { extras: {} }); + const handler2 = Object.assign(vi.fn(), { extras: { qos: 2 } }); untypedServerWithOptions.messageHandlers = objectToMap({ 'telemetry/+': handler0, @@ -175,56 +175,58 @@ describe('ServerMqtt', () => { serverWithOptions.bindEvents(mqttClient); - expect(subscribeSpy.callCount).to.equal(3); + expect(subscribeSpy).toHaveBeenCalledTimes(3); - const calls = subscribeSpy.getCalls(); - const callMap = new Map(calls.map(c => [c.args[0], c.args[1]])); + const calls = subscribeSpy.mock.calls; + const callMap = new Map(calls.map(c => [c[0], c[1]])); - expect(callMap.get('telemetry/+')).to.deep.equal({ qos: 0 }); - expect(callMap.get('events/#')).to.deep.equal({ qos: 1 }); - expect(callMap.get('alerts/critical')).to.deep.equal({ qos: 2 }); + expect(callMap.get('telemetry/+')).toEqual({ qos: 0 }); + expect(callMap.get('events/#')).toEqual({ qos: 1 }); + expect(callMap.get('alerts/critical')).toEqual({ qos: 2 }); }); }); }); describe('getMessageHandler', () => { it(`should return function`, () => { - expect( - typeof server.getMessageHandler(untypedServer.mqttClient), - ).to.be.eql('function'); + expect(typeof server.getMessageHandler(untypedServer.mqttClient)).toEqual( + 'function', + ); }); describe('handler', () => { it('should call "handleMessage"', async () => { - const handleMessageStub = sinon - .stub(server, 'handleMessage') - .callsFake(() => null!); + const handleMessageStub = vi + .spyOn(server, 'handleMessage') + .mockImplementation(() => null!); await server.getMessageHandler(untypedServer.mqttClient)( null!, null!, null!, ); - expect(handleMessageStub.called).to.be.true; + expect(handleMessageStub).toHaveBeenCalled(); }); }); }); describe('handleMessage', () => { - let getPublisherSpy: sinon.SinonSpy; + let getPublisherSpy: ReturnType; const channel = 'test'; const data = 'test'; const id = '3'; beforeEach(() => { - getPublisherSpy = sinon.spy(); - sinon.stub(server, 'getPublisher').callsFake(() => getPublisherSpy); + getPublisherSpy = vi.fn(); + vi.spyOn(server, 'getPublisher').mockImplementation( + () => getPublisherSpy, + ); }); it('should call "handleEvent" if identifier is not present', async () => { - const handleEventSpy = sinon.spy(server, 'handleEvent'); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); await server.handleMessage( channel, Buffer.from(JSON.stringify({ pattern: '', data })), null, ); - expect(handleEventSpy.called).to.be.true; + expect(handleEventSpy).toHaveBeenCalled(); }); it(`should publish NO_MESSAGE_HANDLER if pattern not exists in messageHandlers object`, async () => { await server.handleMessage( @@ -232,16 +234,14 @@ describe('ServerMqtt', () => { Buffer.from(JSON.stringify({ id, pattern: '', data })), null, ); - expect( - getPublisherSpy.calledWith({ - id, - status: 'error', - err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + expect(getPublisherSpy).toHaveBeenCalledWith({ + id, + status: 'error', + err: NO_MESSAGE_HANDLER, + }); }); it(`should call handler with expected arguments`, async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); @@ -251,11 +251,11 @@ describe('ServerMqtt', () => { Buffer.from(JSON.stringify({ pattern: '', data, id: '2' })), null, ); - expect(handler.calledWith(data)).to.be.true; + expect(handler).toHaveBeenCalledWith(data, expect.any(MqttContext)); }); }); describe('getPublisher', () => { - let publisherSpy: sinon.SinonSpy; + let publisherSpy: ReturnType; let pub, publisher; const id = '1'; @@ -263,51 +263,46 @@ describe('ServerMqtt', () => { const context = new MqttContext([pattern, {}]); beforeEach(() => { - publisherSpy = sinon.spy(); + publisherSpy = vi.fn(); pub = { publish: publisherSpy, }; publisher = server.getPublisher(pub, context, id); }); it(`should return function`, () => { - expect(typeof server.getPublisher(null, context, id)).to.be.eql( - 'function', - ); + expect(typeof server.getPublisher(null, context, id)).toEqual('function'); }); it(`should call "publish" with expected arguments`, () => { const respond = 'test'; publisher({ respond, id }); - expect( - publisherSpy.calledWith( - `${pattern}/reply`, - JSON.stringify({ respond, id }), - ), - ).to.be.true; + expect(publisherSpy).toHaveBeenCalledWith( + `${pattern}/reply`, + JSON.stringify({ respond, id }), + {}, + ); }); }); describe('getRequestPattern', () => { const test = 'test'; it(`should leave pattern as it is`, () => { - expect(server.getRequestPattern(test)).to.equal(test); + expect(server.getRequestPattern(test)).toBe(test); }); }); describe('getReplyPattern', () => { const test = 'test'; it(`should append "/reply" to string`, () => { const expectedResult = test + '/reply'; - expect(server.getReplyPattern(test)).to.equal(expectedResult); + expect(server.getReplyPattern(test)).toBe(expectedResult); }); }); describe('parseMessage', () => { it(`should return parsed json`, () => { const obj = { test: 'test' }; - expect(server.parseMessage(obj)).to.deep.equal( - JSON.parse(JSON.stringify(obj)), - ); + expect(server.parseMessage(obj)).toEqual(JSON.parse(JSON.stringify(obj))); }); it(`should not parse argument if it is not an object`, () => { const content = 'test'; - expect(server.parseMessage(content)).to.equal(content); + expect(server.parseMessage(content)).toBe(content); }); }); describe('handleEvent', () => { @@ -315,7 +310,7 @@ describe('ServerMqtt', () => { const data = 'test'; it('should call handler with expected arguments', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); @@ -325,39 +320,44 @@ describe('ServerMqtt', () => { { pattern: '', data }, new BaseRpcContext([]), ); - expect(handler.calledWith(data)).to.be.true; + expect(handler).toHaveBeenCalledWith(data, expect.any(BaseRpcContext)); }); }); describe('matchMqttPattern', () => { it('should return true when topic matches with provided pattern', () => { - expect(server.matchMqttPattern('root/valid/+', 'root/valid/child')).to.be - .true; - expect(server.matchMqttPattern('root/valid/#', 'root/valid/child')).to.be - .true; + expect(server.matchMqttPattern('root/valid/+', 'root/valid/child')).toBe( + true, + ); + expect(server.matchMqttPattern('root/valid/#', 'root/valid/child')).toBe( + true, + ); expect( server.matchMqttPattern('root/valid/#', 'root/valid/child/grandchild'), - ).to.be.true; - expect(server.matchMqttPattern('root/+/child', 'root/valid/child')).to.be - .true; + ).toBe(true); + expect(server.matchMqttPattern('root/+/child', 'root/valid/child')).toBe( + true, + ); }); it('should return false when topic does not matches with provided pattern', () => { - expect(server.matchMqttPattern('root/test/+', 'root/invalid/child')).to.be - .false; - expect(server.matchMqttPattern('root/test/#', 'root/invalid/child')).to.be - .false; + expect(server.matchMqttPattern('root/test/+', 'root/invalid/child')).toBe( + false, + ); + expect(server.matchMqttPattern('root/test/#', 'root/invalid/child')).toBe( + false, + ); expect( server.matchMqttPattern( 'root/#/grandchild', 'root/invalid/child/grandchild', ), - ).to.be.false; + ).toBe(false); expect( server.matchMqttPattern( 'root/+/grandchild', 'root/invalid/child/grandchild', ), - ).to.be.false; + ).toBe(false); }); }); }); diff --git a/packages/microservices/test/server/server-nats.spec.ts b/packages/microservices/test/server/server-nats.spec.ts index 47cf398455b..9550cf10f58 100644 --- a/packages/microservices/test/server/server-nats.spec.ts +++ b/packages/microservices/test/server/server-nats.spec.ts @@ -1,58 +1,68 @@ -import { expect } from 'chai'; -import { JSONCodec } from 'nats'; -import * as sinon from 'sinon'; -import { NO_MESSAGE_HANDLER } from '../../constants'; -import { NatsContext } from '../../ctx-host'; -import { BaseRpcContext } from '../../ctx-host/base-rpc.context'; -import { ServerNats } from '../../server/server-nats'; -import { objectToMap } from './utils/object-to-map'; +import { NO_MESSAGE_HANDLER } from '../../constants.js'; +import { BaseRpcContext } from '../../ctx-host/base-rpc.context.js'; +import { NatsContext } from '../../ctx-host/index.js'; +import { ServerNats } from '../../server/server-nats.js'; +import { objectToMap } from './utils/object-to-map.js'; -// type NatsMsg = import('nats').Msg; +// type NatsMsg = import('@nats-io/nats-core').Msg; type NatsMsg = any; describe('ServerNats', () => { let server: ServerNats; let untypedServer: any; - beforeEach(() => { + beforeEach(async () => { server = new ServerNats({}); untypedServer = server as any; + + // Eagerly init serializer/deserializer (loadPackage is async in ESM) + if (typeof untypedServer.serializer?.init === 'function') { + await untypedServer.serializer.init(); + } + if (typeof untypedServer.deserializer?.init === 'function') { + await untypedServer.deserializer.init(); + } }); describe('listen', () => { let client: any; - let callbackSpy: sinon.SinonSpy; + let callbackSpy: ReturnType; beforeEach(() => { - sinon.stub(server, 'createNatsClient').callsFake(() => client); - callbackSpy = sinon.spy(); + client = { + status: vi.fn().mockReturnValue({ + async *[Symbol.asyncIterator]() {}, + }), + }; + vi.spyOn(server, 'createNatsClient').mockResolvedValue(client); + callbackSpy = vi.fn(); }); - describe('when "start" throws an exception', async () => { + describe('when "start" throws an exception', () => { it('should call callback with a thrown error as an argument', async () => { const error = new Error('random error'); - sinon.stub(server, 'start').callsFake(() => { + vi.spyOn(server, 'start').mockImplementation(() => { throw error; }); - await server.listen(callbackSpy); - expect(callbackSpy.calledWith(error)).to.be.true; + await server.listen(callbackSpy as any); + expect(callbackSpy).toHaveBeenCalledWith(error); }); }); }); describe('close', () => { - const natsClient = { close: sinon.spy() }; + const natsClient = { close: vi.fn() }; beforeEach(() => { untypedServer.natsClient = natsClient; }); it('should close natsClient', async () => { await server.close(); - expect(natsClient.close.called).to.be.true; + expect(natsClient.close).toHaveBeenCalled(); }); describe('when "gracefulShutdown" is true', () => { - const waitForGracePeriod = sinon.spy(); + const waitForGracePeriod = vi.fn(); const subscriptions = [ - { unsubscribe: sinon.spy() }, - { unsubscribe: sinon.spy() }, + { unsubscribe: vi.fn() }, + { unsubscribe: vi.fn() }, ]; beforeEach(() => { (server as any).subscriptions = subscriptions; @@ -63,21 +73,21 @@ describe('ServerNats', () => { it('should unsubscribe all subscriptions', async () => { await server.close(); for (const subscription of subscriptions) { - expect(subscription.unsubscribe.calledOnce).to.be.true; + expect(subscription.unsubscribe).toHaveBeenCalledOnce(); } }); it('should call "waitForGracePeriod"', async () => { await server.close(); - expect(waitForGracePeriod.called).to.be.true; + expect(waitForGracePeriod).toHaveBeenCalled(); }); }); describe('when "gracefulShutdown" is false', () => { - const waitForGracePeriod = sinon.spy(); + const waitForGracePeriod = vi.fn(); const subscriptions = [ - { unsubscribe: sinon.spy() }, - { unsubscribe: sinon.spy() }, + { unsubscribe: vi.fn() }, + { unsubscribe: vi.fn() }, ]; beforeEach(() => { (server as any).subscriptions = subscriptions; @@ -87,24 +97,26 @@ describe('ServerNats', () => { it('should not unsubscribe all subscriptions', async () => { await server.close(); for (const subscription of subscriptions) { - expect(subscription.unsubscribe.called).to.be.false; + expect(subscription.unsubscribe).not.toHaveBeenCalled(); } }); it('should not call "waitForGracePeriod"', async () => { await server.close(); - expect(waitForGracePeriod.called).to.be.false; + expect(waitForGracePeriod).not.toHaveBeenCalled(); }); }); }); describe('bindEvents', () => { - let onSpy: sinon.SinonSpy, subscribeSpy: sinon.SinonSpy, natsClient; + let onSpy: ReturnType, + subscribeSpy: ReturnType, + natsClient; const pattern = 'test'; - const messageHandler = sinon.spy(); + const messageHandler = vi.fn(); beforeEach(() => { - onSpy = sinon.spy(); - subscribeSpy = sinon.spy(); + onSpy = vi.fn(); + subscribeSpy = vi.fn(); natsClient = { on: onSpy, subscribe: subscribeSpy, @@ -116,7 +128,10 @@ describe('ServerNats', () => { it('should subscribe to every pattern', () => { server.bindEvents(natsClient); - expect(subscribeSpy.calledWith(pattern)).to.be.true; + expect(subscribeSpy).toHaveBeenCalledWith( + pattern, + expect.objectContaining({}), + ); }); it('should use a per pattern queue if provided', () => { @@ -129,53 +144,57 @@ describe('ServerNats', () => { }), }); server.bindEvents(natsClient); - const lastCall = subscribeSpy.lastCall; - expect(lastCall.args[1].queue).to.be.eql(queue); + const lastCallArgs = + subscribeSpy.mock.calls[subscribeSpy.mock.calls.length - 1]; + expect(lastCallArgs[1].queue).toEqual(queue); }); it('should fill the subscriptions array properly', () => { server.bindEvents(natsClient); - expect(server['subscriptions'].length).to.be.equals(1); + expect(server['subscriptions'].length).toBe(1); }); }); describe('getMessageHandler', () => { it(`should return function`, () => { - expect(typeof server.getMessageHandler(null!)).to.be.eql('function'); + expect(typeof server.getMessageHandler(null!)).toEqual('function'); }); describe('handler', () => { it('should call "handleMessage"', async () => { - const handleMessageStub = sinon - .stub(server, 'handleMessage') - .callsFake(() => null!); + const handleMessageStub = vi + .spyOn(server, 'handleMessage') + .mockImplementation(() => null!); await server.getMessageHandler('')('' as any, ''); - expect(handleMessageStub.called).to.be.true; + expect(handleMessageStub).toHaveBeenCalled(); }); }); }); describe('handleMessage', () => { - let getPublisherSpy: sinon.SinonSpy; + let getPublisherSpy: ReturnType; const channel = 'test'; const id = '3'; beforeEach(() => { - getPublisherSpy = sinon.spy(); - sinon.stub(server, 'getPublisher').callsFake(() => getPublisherSpy); + getPublisherSpy = vi.fn(); + vi.spyOn(server, 'getPublisher').mockImplementation( + () => getPublisherSpy as any, + ); }); it('should call "handleEvent" if identifier is not present', async () => { - const handleEventSpy = sinon.spy(server, 'handleEvent'); - const data = JSONCodec().encode({ id: 10 }); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); + const data = JSON.stringify({ id: 10 }); const natsMsg: NatsMsg = { data, subject: channel, sid: +id, - respond: sinon.spy(), + respond: vi.fn(), + json: () => JSON.parse(data), }; await server.handleMessage(channel, natsMsg); - expect(handleEventSpy.called).to.be.true; + expect(handleEventSpy).toHaveBeenCalled(); }); it(`should publish NO_MESSAGE_HANDLER if pattern does not exist in messageHandlers object`, async () => { - const data = JSONCodec().encode({ + const data = JSON.stringify({ id, pattern: 'test', data: 'test', @@ -184,20 +203,19 @@ describe('ServerNats', () => { data, subject: channel, sid: +id, - respond: sinon.spy(), + respond: vi.fn(), + json: () => JSON.parse(data), }; await server.handleMessage(channel, natsMsg); - expect( - getPublisherSpy.calledWith({ - id, - status: 'error', - err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + expect(getPublisherSpy).toHaveBeenCalledWith({ + id, + status: 'error', + err: NO_MESSAGE_HANDLER, + }); }); it(`should call handler with expected arguments`, async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); @@ -205,7 +223,7 @@ describe('ServerNats', () => { const headers = {}; const natsContext = new NatsContext([channel, headers]); - const data = JSONCodec().encode({ + const data = JSON.stringify({ pattern: channel, data: 'test', id, @@ -214,11 +232,12 @@ describe('ServerNats', () => { data, subject: channel, sid: +id, - respond: sinon.spy(), + respond: vi.fn(), headers, + json: () => JSON.parse(data), }; await server.handleMessage(channel, natsMsg); - expect(handler.calledWith('test', natsContext)).to.be.true; + expect(handler).toHaveBeenCalledWith('test', natsContext); }); }); describe('getPublisher', () => { @@ -230,9 +249,9 @@ describe('ServerNats', () => { data: new Uint8Array(), subject: '', sid: +id, - respond: sinon.spy(), + respond: vi.fn(), }; - expect(typeof server.getPublisher(natsMsg, id, context)).to.be.eql( + expect(typeof server.getPublisher(natsMsg, id, context)).toEqual( 'function', ); }); @@ -242,15 +261,17 @@ describe('ServerNats', () => { data: new Uint8Array(), subject: '', sid: +id, - respond: sinon.spy(), + respond: vi.fn(), reply: replyTo, - }; + } as NatsMsg; const publisher = server.getPublisher(natsMsg, id, context); const respond = 'test'; publisher({ respond, id }); - expect(natsMsg.respond.calledWith(JSONCodec().encode({ respond, id }))).to - .be.true; + expect(natsMsg.respond).toHaveBeenCalledWith( + JSON.stringify({ respond, id }), + expect.objectContaining({}), + ); }); it(`should not call "publish" when replyTo NOT provided`, () => { const replyTo = undefined; @@ -259,13 +280,13 @@ describe('ServerNats', () => { subject: '', reply: replyTo, sid: +id, - respond: sinon.spy(), - }; + respond: vi.fn(), + } as NatsMsg; const publisher = server.getPublisher(natsMsg, id, context); const respond = 'test'; publisher({ respond, id }); - expect(natsMsg.respond.notCalled); + expect(natsMsg.respond).not.toHaveBeenCalled(); }); }); describe('handleEvent', () => { @@ -273,7 +294,7 @@ describe('ServerNats', () => { const data = 'test'; it('should call handler with expected arguments', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); @@ -283,45 +304,43 @@ describe('ServerNats', () => { { pattern: '', data }, new BaseRpcContext([]), ); - expect(handler.calledWith(data)).to.be.true; + expect(handler).toHaveBeenCalledWith(data, expect.any(BaseRpcContext)); }); }); describe('handleStatusUpdates', () => { - it('should retrieve "status()" async iterator', () => { + it('should retrieve "status()" async iterator', async () => { const serverMock = { - status: sinon.stub().returns({ - [Symbol.asyncIterator]: [], + status: vi.fn().mockReturnValue({ + async *[Symbol.asyncIterator]() {}, }), }; - void server.handleStatusUpdates(serverMock as any); - expect(serverMock.status.called).to.be.true; + await server.handleStatusUpdates(serverMock as any); + expect(serverMock.status).toHaveBeenCalled(); }); it('should log "disconnect" and "error" statuses as "errors"', async () => { - const logErrorSpy = sinon.spy(untypedServer.logger, 'error'); + const logErrorSpy = vi.spyOn(untypedServer.logger, 'error'); const serverMock = { - status: sinon.stub().returns({ + status: vi.fn().mockReturnValue({ async *[Symbol.asyncIterator]() { - yield { type: 'disconnect', data: 'localhost' }; - yield { type: 'error', data: {} }; + yield { type: 'disconnect' }; + yield { type: 'error', error: 'Test error' }; }, }), }; await server.handleStatusUpdates(serverMock as any); - expect(logErrorSpy.calledTwice).to.be.true; - expect( - logErrorSpy.calledWith( - `NatsError: type: "disconnect", data: "localhost".`, - ), + expect(logErrorSpy).toHaveBeenCalledTimes(2); + expect(logErrorSpy).toHaveBeenCalledWith( + `NatsError: type: "disconnect".`, ); - expect( - logErrorSpy.calledWith(`NatsError: type: "disconnect", data: "{}".`), + expect(logErrorSpy).toHaveBeenCalledWith( + `NatsError: type: "error", error: "Test error".`, ); }); it('should log other statuses as "logs"', async () => { - const logSpy = sinon.spy(untypedServer.logger, 'log'); + const logSpy = vi.spyOn(untypedServer.logger, 'log'); const serverMock = { - status: sinon.stub().returns({ + status: vi.fn().mockReturnValue({ async *[Symbol.asyncIterator]() { yield { type: 'non-disconnect', data: 'localhost' }; yield { type: 'warn', data: {} }; @@ -329,13 +348,13 @@ describe('ServerNats', () => { }), }; await server.handleStatusUpdates(serverMock as any); - expect(logSpy.calledTwice).to.be.true; - expect( - logSpy.calledWith( - `NatsStatus: type: "non-disconnect", data: "localhost".`, - ), + expect(logSpy).toHaveBeenCalledTimes(2); + expect(logSpy).toHaveBeenCalledWith( + `NatsStatus: type: "non-disconnect", data: "localhost".`, + ); + expect(logSpy).toHaveBeenCalledWith( + `NatsStatus: type: "warn", data: "{}".`, ); - expect(logSpy.calledWith(`NatsStatus: type: "warn", data: "{}".`)); }); }); }); diff --git a/packages/microservices/test/server/server-redis.spec.ts b/packages/microservices/test/server/server-redis.spec.ts index 402cf87bb9b..f135555fe82 100644 --- a/packages/microservices/test/server/server-redis.spec.ts +++ b/packages/microservices/test/server/server-redis.spec.ts @@ -1,10 +1,8 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { NO_MESSAGE_HANDLER } from '../../constants'; -import { RedisContext } from '../../ctx-host'; -import { BaseRpcContext } from '../../ctx-host/base-rpc.context'; -import { ServerRedis } from '../../server/server-redis'; -import { objectToMap } from './utils/object-to-map'; +import { NO_MESSAGE_HANDLER } from '../../constants.js'; +import { BaseRpcContext } from '../../ctx-host/base-rpc.context.js'; +import { RedisContext } from '../../ctx-host/index.js'; +import { ServerRedis } from '../../server/server-redis.js'; +import { objectToMap } from './utils/object-to-map.js'; describe('ServerRedis', () => { let server: ServerRedis; @@ -15,47 +13,49 @@ describe('ServerRedis', () => { untypedServer = server as any; }); describe('listen', () => { - let onSpy: sinon.SinonSpy; - let connectSpy: sinon.SinonSpy; + let onSpy: ReturnType; + let connectSpy: ReturnType; let client: any; - let callbackSpy: sinon.SinonSpy; + let callbackSpy: ReturnType; beforeEach(() => { - onSpy = sinon.spy(); - connectSpy = sinon.spy(); + onSpy = vi.fn(); + connectSpy = vi.fn(); client = { on: onSpy, connect: connectSpy, }; - sinon.stub(server, 'createRedisClient').callsFake(() => client); + vi.spyOn(server, 'createRedisClient').mockImplementation(() => + Promise.resolve(client), + ); - callbackSpy = sinon.spy(); + callbackSpy = vi.fn(); }); - it('should bind "error" event to handler', () => { - server.listen(callbackSpy); - expect(onSpy.getCall(0).args[0]).to.be.equal('error'); + it('should bind "error" event to handler', async () => { + await server.listen(callbackSpy); + expect(onSpy.mock.calls[0][0]).toBe('error'); }); - it('should call "RedisClient#connect()"', () => { - server.listen(callbackSpy); - expect(connectSpy.called).to.be.true; + it('should call "RedisClient#connect()"', async () => { + await server.listen(callbackSpy); + expect(connectSpy).toHaveBeenCalled(); }); describe('when "start" throws an exception', () => { - it('should call callback with a thrown error as an argument', () => { + it('should call callback with a thrown error as an argument', async () => { const error = new Error('random error'); - const callbackSpy = sinon.spy(); - sinon.stub(server, 'start').callsFake(() => { + const callbackSpy = vi.fn(); + vi.spyOn(server, 'start').mockImplementation(() => { throw error; }); - server.listen(callbackSpy); - expect(callbackSpy.calledWith(error)).to.be.true; + await server.listen(callbackSpy); + expect(callbackSpy).toHaveBeenCalledWith(error); }); }); }); describe('close', () => { - const pub = { quit: sinon.spy() }; - const sub = { quit: sinon.spy() }; + const pub = { quit: vi.fn() }; + const sub = { quit: vi.fn() }; beforeEach(() => { untypedServer.pubClient = pub; untypedServer.subClient = sub; @@ -63,16 +63,19 @@ describe('ServerRedis', () => { it('should close pub & sub server', async () => { await server.close(); - expect(pub.quit.calledOnce).to.be.true; - expect(sub.quit.calledOnce).to.be.true; + expect(pub.quit).toHaveBeenCalledOnce(); + expect(sub.quit).toHaveBeenCalledOnce(); }); }); describe('handleConnection', () => { - let onSpy: sinon.SinonSpy, subscribeSpy: sinon.SinonSpy, sub, psub; + let onSpy: ReturnType, + subscribeSpy: ReturnType, + sub, + psub; beforeEach(() => { - onSpy = sinon.spy(); - subscribeSpy = sinon.spy(); + onSpy = vi.fn(); + subscribeSpy = vi.fn(); sub = { on: onSpy, subscribe: subscribeSpy, @@ -84,24 +87,24 @@ describe('ServerRedis', () => { }); it('should bind "message" event to handler if wildcards are disabled', () => { server.bindEvents(sub, null); - expect(onSpy.getCall(0).args[0]).to.be.equal('message'); + expect(onSpy.mock.calls[0][0]).toBe('message'); }); it('should bind "pmessage" event to handler if wildcards are enabled', () => { untypedServer.options = {}; untypedServer.options.wildcards = true; server.bindEvents(psub, null); - expect(onSpy.getCall(0).args[0]).to.be.equal('pmessage'); + expect(onSpy.mock.calls[0][0]).toBe('pmessage'); }); it('should "subscribe" to each pattern if wildcards are disabled', () => { const pattern = 'test'; - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [pattern]: handler, }); server.bindEvents(sub, null); - expect(subscribeSpy.calledWith(pattern)).to.be.true; + expect(subscribeSpy).toHaveBeenCalledWith(pattern); }); it('should "psubscribe" to each pattern if wildcards are enabled', () => { @@ -109,66 +112,72 @@ describe('ServerRedis', () => { untypedServer.options.wildcards = true; const pattern = 'test'; - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [pattern]: handler, }); server.bindEvents(psub, null); - expect(subscribeSpy.calledWith(pattern)).to.be.true; + expect(subscribeSpy).toHaveBeenCalledWith(pattern); }); }); describe('getMessageHandler', () => { it(`should return function`, () => { - expect(typeof server.getMessageHandler(null)).to.be.eql('function'); + expect(typeof server.getMessageHandler(null)).toEqual('function'); }); }); describe('handleMessage', () => { - let getPublisherSpy: sinon.SinonSpy; + let getPublisherSpy: ReturnType; const channel = 'test'; const data = 'test'; const id = '3'; beforeEach(() => { - getPublisherSpy = sinon.spy(); - sinon.stub(server, 'getPublisher').callsFake(() => getPublisherSpy); + getPublisherSpy = vi.fn(); + vi.spyOn(server, 'getPublisher').mockImplementation( + () => getPublisherSpy, + ); }); it('should call "handleEvent" if identifier is not present', async () => { - const handleEventSpy = sinon.spy(server, 'handleEvent'); - sinon.stub(server, 'parseMessage').callsFake(() => ({ data }) as any); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); + vi.spyOn(server, 'parseMessage').mockImplementation( + () => ({ data }) as any, + ); await server.handleMessage(channel, JSON.stringify({}), null, channel); - expect(handleEventSpy.called).to.be.true; + expect(handleEventSpy).toHaveBeenCalled(); }); it(`should publish NO_MESSAGE_HANDLER if pattern not exists in messageHandlers object`, async () => { - sinon.stub(server, 'parseMessage').callsFake(() => ({ id, data }) as any); + vi.spyOn(server, 'parseMessage').mockImplementation( + () => ({ id, data }) as any, + ); await server.handleMessage( channel, JSON.stringify({ id }), null, channel, ); - expect( - getPublisherSpy.calledWith({ - id, - status: 'error', - err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + expect(getPublisherSpy).toHaveBeenCalledWith({ + id, + status: 'error', + err: NO_MESSAGE_HANDLER, + }); }); it(`should call handler with expected arguments`, async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); - sinon.stub(server, 'parseMessage').callsFake(() => ({ id, data }) as any); + vi.spyOn(server, 'parseMessage').mockImplementation( + () => ({ id, data }) as any, + ); await server.handleMessage(channel, '', null, channel); - expect(handler.calledWith(data)).to.be.true; + expect(handler).toHaveBeenCalledWith(data, expect.any(RedisContext)); }); }); describe('getPublisher', () => { - let publisherSpy: sinon.SinonSpy; + let publisherSpy: ReturnType; let pub, publisher; const id = '1'; @@ -176,64 +185,60 @@ describe('ServerRedis', () => { const context = new RedisContext([] as any); beforeEach(() => { - publisherSpy = sinon.spy(); + publisherSpy = vi.fn(); pub = { publish: publisherSpy, }; publisher = server.getPublisher(pub, pattern, id, context); }); it(`should return function`, () => { - expect(typeof server.getPublisher(null, null, id, context)).to.be.eql( + expect(typeof server.getPublisher(null, null, id, context)).toEqual( 'function', ); }); it(`should call "publish" with expected arguments`, () => { const respond = 'test'; publisher({ respond, id }); - expect( - publisherSpy.calledWith( - `${pattern}.reply`, - JSON.stringify({ respond, id }), - ), - ).to.be.true; + expect(publisherSpy).toHaveBeenCalledWith( + `${pattern}.reply`, + JSON.stringify({ respond, id }), + ); }); }); describe('parseMessage', () => { it(`should return parsed json`, () => { const obj = { test: 'test' }; - expect(server.parseMessage(obj)).to.deep.equal( - JSON.parse(JSON.stringify(obj)), - ); + expect(server.parseMessage(obj)).toEqual(JSON.parse(JSON.stringify(obj))); }); it(`should not parse argument if it is not an object`, () => { const content = 'test'; - expect(server.parseMessage(content)).to.equal(content); + expect(server.parseMessage(content)).toBe(content); }); }); describe('getRequestPattern', () => { const test = 'test'; it(`should leave pattern as it is`, () => { const expectedResult = test; - expect(server.getRequestPattern(test)).to.equal(expectedResult); + expect(server.getRequestPattern(test)).toBe(expectedResult); }); }); describe('getReplyPattern', () => { const test = 'test'; it(`should append ".reply" to string`, () => { const expectedResult = test + '.reply'; - expect(server.getReplyPattern(test)).to.equal(expectedResult); + expect(server.getReplyPattern(test)).toBe(expectedResult); }); }); describe('getClientOptions', () => { it('should return options object with "retryStrategy" and call "createRetryStrategy"', () => { - const createSpy = sinon.spy(server, 'createRetryStrategy'); + const createSpy = vi.spyOn(server, 'createRetryStrategy'); const { retryStrategy } = server.getClientOptions()!; try { retryStrategy!(0); } catch { // Ignore } - expect(createSpy.called).to.be.true; + expect(createSpy).toHaveBeenCalled(); }); }); describe('createRetryStrategy', () => { @@ -241,7 +246,7 @@ describe('ServerRedis', () => { it('should return undefined', () => { untypedServer.isManuallyClosed = true; const result = server.createRetryStrategy(0); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('when "retryAttempts" does not exist', () => { @@ -249,7 +254,7 @@ describe('ServerRedis', () => { untypedServer.options.options = {}; untypedServer.options.options.retryAttempts = undefined; - expect(server.createRetryStrategy(4)).to.be.undefined; + expect(server.createRetryStrategy(4)).toBeUndefined(); }); }); describe('when "attempts" count is max', () => { @@ -257,7 +262,7 @@ describe('ServerRedis', () => { untypedServer.options.options = {}; untypedServer.options.options.retryAttempts = 3; - expect(server.createRetryStrategy(4)).to.be.undefined; + expect(server.createRetryStrategy(4)).toBeUndefined(); }); }); describe('otherwise', () => { @@ -267,7 +272,7 @@ describe('ServerRedis', () => { untypedServer.options.retryAttempts = 3; untypedServer.options.retryDelay = 3; const result = server.createRetryStrategy(2); - expect(result).to.be.eql(untypedServer.options.retryDelay); + expect(result).toEqual(untypedServer.options.retryDelay); }); }); }); @@ -276,7 +281,7 @@ describe('ServerRedis', () => { const data = 'test'; it('should call handler with expected arguments', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); @@ -286,7 +291,7 @@ describe('ServerRedis', () => { { pattern: '', data }, new BaseRpcContext([]), ); - expect(handler.calledWith(data)).to.be.true; + expect(handler).toHaveBeenCalledWith(data, expect.any(BaseRpcContext)); }); }); }); diff --git a/packages/microservices/test/server/server-rmq.spec.ts b/packages/microservices/test/server/server-rmq.spec.ts index 2872891bc60..ef417b14d9e 100644 --- a/packages/microservices/test/server/server-rmq.spec.ts +++ b/packages/microservices/test/server/server-rmq.spec.ts @@ -1,9 +1,7 @@ -import { assert, expect } from 'chai'; -import * as sinon from 'sinon'; -import { NO_MESSAGE_HANDLER, RQM_DEFAULT_QUEUE } from '../../constants'; -import { RmqContext } from '../../ctx-host'; -import { ServerRMQ } from '../../server/server-rmq'; -import { objectToMap } from './utils/object-to-map'; +import { NO_MESSAGE_HANDLER, RMQ_DEFAULT_QUEUE } from '../../constants.js'; +import { RmqContext } from '../../ctx-host/index.js'; +import { ServerRMQ } from '../../server/server-rmq.js'; +import { objectToMap } from './utils/object-to-map.js'; describe('ServerRMQ', () => { let server: ServerRMQ; @@ -15,64 +13,68 @@ describe('ServerRMQ', () => { }); describe('listen', () => { - let createClient: sinon.SinonStub; - let onStub: sinon.SinonStub; - let createChannelStub: sinon.SinonStub; - let setupChannelStub: sinon.SinonStub; + let createClient: ReturnType; + let onStub: ReturnType; + let createChannelStub: ReturnType; + let setupChannelStub: ReturnType; let client: any; - let callbackSpy: sinon.SinonSpy; + let callbackSpy: ReturnType; beforeEach(() => { - onStub = sinon - .stub() - .callsFake((event, callback) => event === 'connect' && callback()); - createChannelStub = sinon.stub().callsFake(({ setup }) => setup()); - setupChannelStub = sinon - .stub(server, 'setupChannel') - .callsFake(() => ({}) as any); + onStub = vi + .fn() + .mockImplementation( + (event, callback) => event === 'connect' && callback(), + ); + createChannelStub = vi.fn().mockImplementation(({ setup }) => setup()); + setupChannelStub = vi + .spyOn(server, 'setupChannel') + .mockImplementation(() => ({}) as any); client = { on: onStub, once: onStub, createChannel: createChannelStub, }; - createClient = sinon.stub(server, 'createClient').callsFake(() => client); - callbackSpy = sinon.spy(); + createClient = vi + .spyOn(server, 'createClient') + .mockImplementation(() => client); + callbackSpy = vi.fn(); }); afterEach(() => { - setupChannelStub.restore(); + setupChannelStub.mockRestore(); }); it('should call "createClient"', async () => { await server.listen(callbackSpy); - expect(createClient.called).to.be.true; + expect(createClient).toHaveBeenCalled(); }); it('should bind "connect" event to handler', async () => { await server.listen(callbackSpy); - expect(onStub.getCall(0).args[0]).to.be.equal('connect'); + expect(onStub.mock.calls[0][0]).toBe('connect'); }); it('should bind "disconnected" event to handler', async () => { await server.listen(callbackSpy); - expect(onStub.getCall(2).args[0]).to.be.equal('disconnect'); + expect(onStub.mock.calls[2][0]).toBe('disconnect'); }); it('should bind "connectFailed" event to handler', async () => { await server.listen(callbackSpy); - expect(onStub.getCall(3).args[0]).to.be.equal('connectFailed'); + expect(onStub.mock.calls[3][0]).toBe('connectFailed'); }); describe('when "start" throws an exception', () => { it('should call callback with a thrown error as an argument', async () => { const error = new Error('random error'); - sinon.stub(server, 'start').callsFake(() => { + vi.spyOn(server, 'start').mockImplementation(() => { throw error; }); await server.listen(callbackSpy); - expect(callbackSpy.calledWith(error)).to.be.true; + expect(callbackSpy).toHaveBeenCalledWith(error); }); }); }); describe('close', () => { - const rmqServer = { close: sinon.spy() }; - const rmqChannel = { close: sinon.spy() }; + const rmqServer = { close: vi.fn() }; + const rmqChannel = { close: vi.fn() }; beforeEach(() => { untypedServer.server = rmqServer; @@ -80,11 +82,11 @@ describe('ServerRMQ', () => { }); it('should close server', async () => { await server.close(); - expect(rmqServer.close.called).to.be.true; + expect(rmqServer.close).toHaveBeenCalled(); }); it('should close channel', async () => { await server.close(); - expect(rmqChannel.close.called).to.be.true; + expect(rmqChannel.close).toHaveBeenCalled(); }); }); @@ -102,40 +104,45 @@ describe('ServerRMQ', () => { id: '3', }); const channel = { - nack: sinon.spy(), + nack: vi.fn(), }; - let sendMessageStub: sinon.SinonStub; + let sendMessageStub: ReturnType; beforeEach(() => { - sendMessageStub = sinon.stub(server, 'sendMessage').callsFake(() => ({})); + sendMessageStub = vi + .spyOn(server, 'sendMessage') + .mockImplementation(() => ({})); untypedServer.channel = channel; }); afterEach(() => { - channel.nack.resetHistory(); + channel.nack.mockReset(); }); it('should call "handleEvent" if identifier is not present', async () => { - const handleEventSpy = sinon.spy(server, 'handleEvent'); + const handleEventSpy = vi.spyOn(server, 'handleEvent'); await server.handleMessage(createMessage({ pattern: '', data: '' }), ''); - expect(handleEventSpy.called).to.be.true; + expect(handleEventSpy).toHaveBeenCalled(); }); it('should send NO_MESSAGE_HANDLER error if key does not exists in handlers object', async () => { await server.handleMessage(msg, ''); - expect( - sendMessageStub.calledWith({ + expect(sendMessageStub).toHaveBeenCalledWith( + { id: '3', status: 'error', err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + }, + undefined, + 1, + expect.any(RmqContext), + ); }); it('should call handler if exists in handlers object', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [pattern]: handler as any, }); await server.handleMessage(msg, ''); - expect(handler.calledOnce).to.be.true; + expect(handler).toHaveBeenCalledOnce(); }); it('should not throw if the message is an invalid json', async () => { const invalidMsg = { @@ -144,37 +151,43 @@ describe('ServerRMQ', () => { }, properties: { correlationId: 1 }, }; - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [pattern]: handler as any, }); return server.handleMessage(invalidMsg, '').catch(() => { - assert.fail('Was not supposed to throw an error'); + throw new Error('Was not supposed to throw an error'); }); }); it('should negative acknowledge if message does not exists in handlers object and noAck option is false', async () => { untypedServer.noAck = false; await server.handleMessage(msg, ''); - expect(channel.nack.calledWith(msg, false, false)).to.be.true; - expect( - sendMessageStub.calledWith({ + expect(channel.nack).toHaveBeenCalledWith(msg, false, false); + expect(sendMessageStub).toHaveBeenCalledWith( + { id: '3', status: 'error', err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + }, + undefined, + 1, + expect.any(RmqContext), + ); }); it('should not negative acknowledge if key does not exists in handlers object and noAck option is true', async () => { await server.handleMessage(msg, ''); - expect(channel.nack.notCalled).to.be.true; - expect( - sendMessageStub.calledWith({ + expect(channel.nack).not.toHaveBeenCalled(); + expect(sendMessageStub).toHaveBeenCalledWith( + { id: '3', status: 'error', err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + }, + undefined, + 1, + expect.any(RmqContext), + ); }); }); describe('setupChannel', () => { @@ -195,25 +208,27 @@ describe('ServerRMQ', () => { }; channel = { - assertQueue: sinon.spy(() => ({ queue })), - prefetch: sinon.spy(), - consume: sinon.spy(), - assertExchange: sinon.spy(() => ({})), - bindQueue: sinon.spy(), + assertQueue: vi.fn(() => ({ queue })), + prefetch: vi.fn(), + consume: vi.fn(), + assertExchange: vi.fn(() => ({})), + bindQueue: vi.fn(), }; }); it('should call "assertQueue" with queue and queue options when noAssert is false', async () => { server['noAssert' as any] = false; await server.setupChannel(channel, () => null); - expect(channel.assertQueue.calledWith(queue, queueOptions)).to.be.true; + expect(channel.assertQueue).toHaveBeenCalledWith(queue, queueOptions); }); it('should call "assertQueue" with queue and queue options when queue is default queue', async () => { - server['queue' as any] = RQM_DEFAULT_QUEUE; + server['queue' as any] = RMQ_DEFAULT_QUEUE; await server.setupChannel(channel, () => null); - expect(channel.assertQueue.calledWith(RQM_DEFAULT_QUEUE, queueOptions)).to - .be.true; + expect(channel.assertQueue).toHaveBeenCalledWith( + RMQ_DEFAULT_QUEUE, + queueOptions, + ); }); it('should not call "assertQueue" when noAssert is true', async () => { server['options' as any] = { @@ -222,33 +237,35 @@ describe('ServerRMQ', () => { }; await server.setupChannel(channel, () => null); - expect(channel.assertQueue.called).not.to.be.true; + expect(channel.assertQueue).not.toHaveBeenCalled(); }); it('should call "bindQueue" with exchangeType is fanout', async () => { const namedQueue = 'exclusive-queue-name'; - channel.assertQueue = sinon.spy(() => ({ queue: namedQueue })); - server['queue' as any] = RQM_DEFAULT_QUEUE; + channel.assertQueue = vi.fn(() => ({ queue: namedQueue })); + server['queue' as any] = RMQ_DEFAULT_QUEUE; server['options' as any] = { ...(server as any)['options'], exchangeType: 'fanout', exchange: exchange, }; await server.setupChannel(channel, () => null); - expect(channel.bindQueue.calledWith(namedQueue, exchange, '')).to.be.true; + expect(channel.bindQueue).toHaveBeenCalledWith(namedQueue, exchange, ''); }); it('should call "prefetch" with prefetchCount and "isGlobalPrefetchCount"', async () => { await server.setupChannel(channel, () => null); - expect(channel.prefetch.calledWith(prefetchCount, isGlobalPrefetchCount)) - .to.be.true; + expect(channel.prefetch).toHaveBeenCalledWith( + prefetchCount, + isGlobalPrefetchCount, + ); }); it('should call "consumeChannel" method', async () => { await server.setupChannel(channel, () => null); - expect(channel.consume.called).to.be.true; + expect(channel.consume).toHaveBeenCalled(); }); it('should call "resolve" function', async () => { - const resolve = sinon.spy(); + const resolve = vi.fn(); await server.setupChannel(channel, resolve); - expect(resolve.called).to.be.true; + expect(resolve).toHaveBeenCalled(); }); }); @@ -259,7 +276,7 @@ describe('ServerRMQ', () => { beforeEach(() => { channel = { - sendToQueue: sinon.spy(), + sendToQueue: vi.fn(), }; server['channel'] = channel; }); @@ -270,13 +287,11 @@ describe('ServerRMQ', () => { const correlationId = '0'; server.sendMessage(message, replyTo, correlationId, context); - expect( - channel.sendToQueue.calledWith( - replyTo, - Buffer.from(JSON.stringify(message)), - { correlationId }, - ), - ).to.be.true; + expect(channel.sendToQueue).toHaveBeenCalledWith( + replyTo, + Buffer.from(JSON.stringify(message)), + { correlationId }, + ); }); }); @@ -285,7 +300,7 @@ describe('ServerRMQ', () => { const data = 'test'; it('should call handler with expected arguments', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); @@ -295,11 +310,11 @@ describe('ServerRMQ', () => { { pattern: '', data }, new RmqContext([{}, {}, '']), ); - expect(handler.calledWith(data)).to.be.true; + expect(handler).toHaveBeenCalledWith(data, expect.any(RmqContext)); }); it('should negative acknowledge without retrying if key does not exists in handlers object and noAck option is false', async () => { - const nack = sinon.spy(); + const nack = vi.fn(); const message = { pattern: 'no-exists', data }; untypedServer.channel = { nack, @@ -311,11 +326,11 @@ describe('ServerRMQ', () => { new RmqContext([message, '', '']), ); - expect(nack.calledWith(message, false, false)).to.be.true; + expect(nack).toHaveBeenCalledWith(message, false, false); }); it('should not negative acknowledge if key does not exists in handlers object but noAck option is true', async () => { - const nack = sinon.spy(); + const nack = vi.fn(); const message = { pattern: 'no-exists', data }; untypedServer.channel = { nack, @@ -327,7 +342,7 @@ describe('ServerRMQ', () => { new RmqContext([message, '', '']), ); - expect(nack.calledWith(message, false, false)).not.to.be.true; + expect(nack).not.toHaveBeenCalledWith(message, false, false); }); }); @@ -340,42 +355,44 @@ describe('ServerRMQ', () => { describe('exact matches', () => { it('should match identical patterns', () => { - expect(matchRmqPattern('user.created', 'user.created')).to.be.true; - expect(matchRmqPattern('order.updated', 'order.updated')).to.be.true; + expect(matchRmqPattern('user.created', 'user.created')).toBe(true); + expect(matchRmqPattern('order.updated', 'order.updated')).toBe(true); }); it('should not match different patterns', () => { - expect(matchRmqPattern('user.created', 'user.updated')).to.be.false; - expect(matchRmqPattern('order.created', 'user.created')).to.be.false; + expect(matchRmqPattern('user.created', 'user.updated')).toBe(false); + expect(matchRmqPattern('order.created', 'user.created')).toBe(false); }); it('should handle patterns with $ character (original issue)', () => { expect( matchRmqPattern('$internal.plugin.status', '$internal.plugin.status'), - ).to.be.true; + ).toBe(true); expect( matchRmqPattern( '$internal.plugin.0.status', '$internal.plugin.0.status', ), - ).to.be.true; - expect(matchRmqPattern('user.$special.event', 'user.$special.event')).to - .be.true; + ).toBe(true); + expect( + matchRmqPattern('user.$special.event', 'user.$special.event'), + ).toBe(true); }); }); describe('single wildcard (*)', () => { it('should match single segments', () => { - expect(matchRmqPattern('user.*', 'user.created')).to.be.true; - expect(matchRmqPattern('user.*', 'user.updated')).to.be.true; - expect(matchRmqPattern('*.created', 'user.created')).to.be.true; - expect(matchRmqPattern('*.created', 'order.created')).to.be.true; + expect(matchRmqPattern('user.*', 'user.created')).toBe(true); + expect(matchRmqPattern('user.*', 'user.updated')).toBe(true); + expect(matchRmqPattern('*.created', 'user.created')).toBe(true); + expect(matchRmqPattern('*.created', 'order.created')).toBe(true); }); it('should not match when segment counts differ', () => { - expect(matchRmqPattern('user.*', 'user.profile.created')).to.be.false; - expect(matchRmqPattern('*.created', 'user.profile.created')).to.be - .false; + expect(matchRmqPattern('user.*', 'user.profile.created')).toBe(false); + expect(matchRmqPattern('*.created', 'user.profile.created')).toBe( + false, + ); }); it('should handle patterns with $ and *', () => { @@ -384,61 +401,66 @@ describe('ServerRMQ', () => { '$internal.plugin.*.status', '$internal.plugin.0.status', ), - ).to.be.true; + ).toBe(true); expect( matchRmqPattern( '$internal.plugin.*.status', '$internal.plugin.1.status', ), - ).to.be.true; - expect(matchRmqPattern('$internal.*.status', '$internal.plugin.status')) - .to.be.true; + ).toBe(true); + expect( + matchRmqPattern('$internal.*.status', '$internal.plugin.status'), + ).toBe(true); }); it('should handle multiple * wildcards', () => { - expect(matchRmqPattern('*.*.created', 'user.profile.created')).to.be - .true; - expect(matchRmqPattern('*.*.created', 'order.item.created')).to.be.true; - expect(matchRmqPattern('*.*.created', 'user.created')).to.be.false; + expect(matchRmqPattern('*.*.created', 'user.profile.created')).toBe( + true, + ); + expect(matchRmqPattern('*.*.created', 'order.item.created')).toBe(true); + expect(matchRmqPattern('*.*.created', 'user.created')).toBe(false); }); }); describe('catch all wildcard (#)', () => { it('should match when # is at the end', () => { - expect(matchRmqPattern('user.#', 'user.created')).to.be.true; - expect(matchRmqPattern('user.#', 'user.profile.created')).to.be.true; - expect(matchRmqPattern('user.#', 'user.profile.details.updated')).to.be - .true; + expect(matchRmqPattern('user.#', 'user.created')).toBe(true); + expect(matchRmqPattern('user.#', 'user.profile.created')).toBe(true); + expect(matchRmqPattern('user.#', 'user.profile.details.updated')).toBe( + true, + ); }); it('should handle patterns with $ and #', () => { - expect(matchRmqPattern('$internal.#', '$internal.plugin.status')).to.be - .true; - expect(matchRmqPattern('$internal.#', '$internal.plugin.0.status')).to - .be.true; + expect(matchRmqPattern('$internal.#', '$internal.plugin.status')).toBe( + true, + ); + expect( + matchRmqPattern('$internal.#', '$internal.plugin.0.status'), + ).toBe(true); expect( matchRmqPattern('$internal.plugin.#', '$internal.plugin.0.status'), - ).to.be.true; + ).toBe(true); }); it('should handle # at the beginning', () => { - expect(matchRmqPattern('#', 'user.created')).to.be.true; - expect(matchRmqPattern('#', 'user.profile.created')).to.be.true; - expect(matchRmqPattern('#', 'created')).to.be.true; + expect(matchRmqPattern('#', 'user.created')).toBe(true); + expect(matchRmqPattern('#', 'user.profile.created')).toBe(true); + expect(matchRmqPattern('#', 'created')).toBe(true); }); }); describe('edge cases', () => { it('should handle empty routing key', () => { - expect(matchRmqPattern('user.created', '')).to.be.false; - expect(matchRmqPattern('*', '')).to.be.false; - expect(matchRmqPattern('#', '')).to.be.true; + expect(matchRmqPattern('user.created', '')).toBe(false); + expect(matchRmqPattern('*', '')).toBe(false); + expect(matchRmqPattern('#', '')).toBe(true); }); it('should handle single segments', () => { - expect(matchRmqPattern('user', 'user')).to.be.true; - expect(matchRmqPattern('*', 'user')).to.be.true; - expect(matchRmqPattern('#', 'user')).to.be.true; + expect(matchRmqPattern('user', 'user')).toBe(true); + expect(matchRmqPattern('*', 'user')).toBe(true); + expect(matchRmqPattern('#', 'user')).toBe(true); }); it('should handle complex $ patterns that previously failed', () => { @@ -447,10 +469,11 @@ describe('ServerRMQ', () => { '$exchange.*.routing.#', '$exchange.topic.routing.key.test', ), - ).to.be.true; - expect(matchRmqPattern('$sys.#', '$sys.broker.clients')).to.be.true; - expect(matchRmqPattern('$SYS.#', '$SYS.broker.load.messages.received')) - .to.be.true; + ).toBe(true); + expect(matchRmqPattern('$sys.#', '$sys.broker.clients')).toBe(true); + expect( + matchRmqPattern('$SYS.#', '$SYS.broker.load.messages.received'), + ).toBe(true); }); }); }); diff --git a/packages/microservices/test/server/server-tcp.spec.ts b/packages/microservices/test/server/server-tcp.spec.ts index fb54d900136..0a3a50a5b9d 100644 --- a/packages/microservices/test/server/server-tcp.spec.ts +++ b/packages/microservices/test/server/server-tcp.spec.ts @@ -1,11 +1,9 @@ -import { expect } from 'chai'; import { Socket as NetSocket } from 'net'; -import * as sinon from 'sinon'; -import { NO_MESSAGE_HANDLER } from '../../constants'; -import { BaseRpcContext } from '../../ctx-host/base-rpc.context'; -import { TcpSocket } from '../../helpers/tcp-socket'; -import { ServerTCP } from '../../server/server-tcp'; -import { objectToMap } from './utils/object-to-map'; +import { NO_MESSAGE_HANDLER } from '../../constants.js'; +import { BaseRpcContext } from '../../ctx-host/base-rpc.context.js'; +import { TcpSocket } from '../../helpers/tcp-socket.js'; +import { ServerTCP } from '../../server/server-tcp.js'; +import { objectToMap } from './utils/object-to-map.js'; describe('ServerTCP', () => { let server: ServerTCP; @@ -17,41 +15,41 @@ describe('ServerTCP', () => { }); describe('bindHandler', () => { - const socket = { on: sinon.spy() }; + const socket = { on: vi.fn() }; beforeEach(() => { - sinon.stub(server, 'getSocketInstance' as any).callsFake(() => socket); + vi.spyOn(server, 'getSocketInstance' as any).mockImplementation( + () => socket, + ); }); it('should bind message and error events to handler', () => { server.bindHandler(null!); - expect(socket.on.calledTwice).to.be.true; + expect(socket.on).toHaveBeenCalledTimes(2); }); }); describe('close', () => { - const tcpServer = { close: sinon.spy() }; + const tcpServer = { close: vi.fn() }; beforeEach(() => { untypedServer.server = tcpServer; }); it('should close server', () => { server.close(); - expect(tcpServer.close.called).to.be.true; + expect(tcpServer.close).toHaveBeenCalled(); }); }); describe('listen', () => { - const serverMock = { listen: sinon.spy(), once: sinon.spy() }; + const serverMock = { listen: vi.fn(), once: vi.fn() }; beforeEach(() => { untypedServer.server = serverMock; }); it('should call native listen method with expected arguments', () => { const callback = () => {}; server.listen(callback); - expect( - serverMock.listen.calledWith( - untypedServer.port, - untypedServer.host, - callback, - ), - ).to.be.true; + expect(serverMock.listen).toHaveBeenCalledWith( + untypedServer.port, + untypedServer.host, + callback, + ); }); }); describe('handleMessage', () => { @@ -63,26 +61,24 @@ describe('ServerTCP', () => { }; beforeEach(() => { socket = { - sendMessage: sinon.spy(), + sendMessage: vi.fn(), }; }); it('should send NO_MESSAGE_HANDLER error if key does not exists in handlers object', async () => { await server.handleMessage(socket, msg); - expect( - socket.sendMessage.calledWith({ - id: msg.id, - status: 'error', - err: NO_MESSAGE_HANDLER, - }), - ).to.be.true; + expect(socket.sendMessage).toHaveBeenCalledWith({ + id: msg.id, + status: 'error', + err: NO_MESSAGE_HANDLER, + }); }); it('should call handler if exists in handlers object', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [msg.pattern]: handler as any, }); await server.handleMessage(socket, msg); - expect(handler.calledOnce).to.be.true; + expect(handler).toHaveBeenCalledOnce(); }); }); describe('handleClose', () => { @@ -90,14 +86,14 @@ describe('ServerTCP', () => { it('should return undefined', () => { untypedServer.isExplicitlyTerminated = true; const result = server.handleClose(); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('when "retryAttempts" does not exist', () => { it('should return undefined', () => { untypedServer.options.retryAttempts = undefined; const result = server.handleClose(); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('when "retryAttemptsCount" count is max', () => { @@ -105,7 +101,7 @@ describe('ServerTCP', () => { untypedServer.options.retryAttempts = 3; untypedServer.retryAttemptsCount = 3; const result = server.handleClose(); - expect(result).to.be.undefined; + expect(result).toBeUndefined(); }); }); describe('otherwise', () => { @@ -116,7 +112,7 @@ describe('ServerTCP', () => { untypedServer.retryAttemptsCount = 2; untypedServer.options.retryDelay = 3; const result = server.handleClose(); - expect(result).to.be.not.undefined; + expect(result).toBeDefined(); }); }); }); @@ -126,7 +122,7 @@ describe('ServerTCP', () => { const data = 'test'; it('should call handler with expected arguments', async () => { - const handler = sinon.spy(); + const handler = vi.fn(); untypedServer.messageHandlers = objectToMap({ [channel]: handler, }); @@ -136,7 +132,7 @@ describe('ServerTCP', () => { { pattern: '', data }, new BaseRpcContext([]), ); - expect(handler.calledWith(data)).to.be.true; + expect(handler).toHaveBeenCalledWith(data, expect.any(BaseRpcContext)); }); }); @@ -148,7 +144,7 @@ describe('ServerTCP', () => { const server = new ServerTCP({}); const socket = new NetSocket(); const jsonSocket = server['getSocketInstance'](socket); - expect(jsonSocket['maxBufferSize']).to.equal(DEFAULT_MAX_BUFFER_SIZE); + expect(jsonSocket['maxBufferSize']).toBe(DEFAULT_MAX_BUFFER_SIZE); }); }); @@ -158,7 +154,7 @@ describe('ServerTCP', () => { const server = new ServerTCP({ maxBufferSize: customSize }); const socket = new NetSocket(); const jsonSocket = server['getSocketInstance'](socket); - expect(jsonSocket['maxBufferSize']).to.equal(customSize); + expect(jsonSocket['maxBufferSize']).toBe(customSize); }); it('should pass maxBufferSize to JsonSocket', () => { @@ -166,7 +162,7 @@ describe('ServerTCP', () => { const server = new ServerTCP({ maxBufferSize: customSize }); const socket = new NetSocket(); const jsonSocket = server['getSocketInstance'](socket); - expect(jsonSocket['maxBufferSize']).to.equal(customSize); + expect(jsonSocket['maxBufferSize']).toBe(customSize); }); }); @@ -186,9 +182,9 @@ describe('ServerTCP', () => { }); const socket = new NetSocket(); const customSocket = server['getSocketInstance'](socket); - expect(customSocket).to.be.instanceOf(CustomSocket); + expect(customSocket).toBeInstanceOf(CustomSocket); // Custom socket should not have maxBufferSize property - expect(customSocket['maxBufferSize']).to.be.undefined; + expect(customSocket['maxBufferSize']).toBeUndefined(); }); }); }); diff --git a/packages/microservices/test/server/server.spec.ts b/packages/microservices/test/server/server.spec.ts index 3c7fe801fc3..1a483cc8821 100644 --- a/packages/microservices/test/server/server.spec.ts +++ b/packages/microservices/test/server/server.spec.ts @@ -1,7 +1,5 @@ -import { expect } from 'chai'; import { throwError as _throw, lastValueFrom, Observable, of } from 'rxjs'; -import * as sinon from 'sinon'; -import { Server } from '../../server/server'; +import { Server } from '../../server/server.js'; class TestServer extends Server { public on< @@ -18,36 +16,33 @@ class TestServer extends Server { describe('Server', () => { const server = new TestServer(); const untypedServer = server as any; - const sandbox = sinon.createSandbox(); const callback = () => {}, pattern = { test: 'test pattern' }; afterEach(() => { - sandbox.restore(); + vi.restoreAllMocks(); }); describe('addHandler', () => { it(`should add handler`, () => { const handlerRoute = 'hello'; - sandbox - .stub(server as any, 'messageHandlers') - .value({ set() {}, has() {} }); + untypedServer.messageHandlers = new Map(); - const messageHandlersSetSpy = sinon.spy( + const messageHandlersSetSpy = vi.spyOn( untypedServer.messageHandlers, 'set', ); - const normalizePatternStub = sinon - .stub(server as any, 'normalizePattern') - .returns(handlerRoute); + const normalizePatternStub = vi + .spyOn(server as any, 'normalizePattern') + .mockReturnValue(handlerRoute); server.addHandler(pattern, callback as any); - expect(messageHandlersSetSpy.called).to.be.true; - expect(messageHandlersSetSpy.args[0][0]).to.be.equal(handlerRoute); - expect(messageHandlersSetSpy.args[0][1]).to.be.equal(callback); + expect(messageHandlersSetSpy).toHaveBeenCalled(); + expect(messageHandlersSetSpy.mock.calls[0][0]).toBe(handlerRoute); + expect(messageHandlersSetSpy.mock.calls[0][1]).toBe(callback); - normalizePatternStub.restore(); + normalizePatternStub.mockRestore(); }); describe('when handler is an event handler', () => { describe('and there are other handlers registered for the pattern already', () => { @@ -60,28 +55,30 @@ describe('Server', () => { untypedServer['messageHandlers'] = new Map([ [handlerRoute, headHandler], ]); - const normalizePatternStub = sinon - .stub(server as any, 'normalizePattern') - .returns(handlerRoute); + const normalizePatternStub = vi + .spyOn(server as any, 'normalizePattern') + .mockReturnValue(handlerRoute); server.addHandler(pattern, callback as any, true); - expect(nextHandler.next).to.equal(callback); - normalizePatternStub.restore(); + expect(nextHandler.next).toBe(callback); + normalizePatternStub.mockRestore(); }); }); }); }); describe('getRouteFromPattern', () => { - let normalizePatternStub: sinon.SinonStub; + let normalizePatternStub: ReturnType; beforeEach(() => { - normalizePatternStub = sinon.stub(server as any, 'normalizePattern'); + normalizePatternStub = vi + .spyOn(server as any, 'normalizePattern') + .mockImplementation(() => ({}) as any); }); afterEach(() => { - normalizePatternStub.restore(); + normalizePatternStub.mockRestore(); }); describe(`when gets 'string' pattern`, () => { @@ -90,7 +87,7 @@ describe('Server', () => { const transformedServerPattern = inputServerPattern; untypedServer.getRouteFromPattern(inputServerPattern); - expect(normalizePatternStub.args[0][0]).to.be.equal( + expect(normalizePatternStub.mock.calls[0][0]).toBe( transformedServerPattern, ); }); @@ -105,7 +102,7 @@ describe('Server', () => { }; untypedServer.getRouteFromPattern(inputServerPattern); - expect(normalizePatternStub.args[0][0]).to.be.deep.equal( + expect(normalizePatternStub.mock.calls[0][0]).toEqual( transformedServerPattern, ); }); @@ -114,26 +111,23 @@ describe('Server', () => { describe('send', () => { let stream$: Observable; - let sendSpy: sinon.SinonSpy; + let sendSpy: ReturnType; beforeEach(() => { stream$ = of('test'); }); describe('when stream', () => { beforeEach(() => { - sendSpy = sinon.spy(); + sendSpy = vi.fn(); }); describe('throws exception', () => { beforeEach(() => { server.send(_throw(() => 'test') as any, sendSpy); }); - it('should send error and complete', () => { - process.nextTick(() => { - expect( - sendSpy.calledWith({ - err: 'test', - isDisposed: true, - }), - ).to.be.true; + it('should send error and complete', async () => { + await new Promise(resolve => process.nextTick(resolve)); + expect(sendSpy).toHaveBeenCalledWith({ + err: 'test', + isDisposed: true, }); }); }); @@ -141,14 +135,11 @@ describe('Server', () => { beforeEach(() => { server.send(stream$, sendSpy); }); - it('should send response and "complete" event', () => { - process.nextTick(() => { - expect( - sendSpy.calledWith({ - response: 'test', - isDisposed: true, - }), - ).to.be.true; + it('should send response and "complete" event', async () => { + await new Promise(resolve => process.nextTick(resolve)); + expect(sendSpy).toHaveBeenCalledWith({ + response: 'test', + isDisposed: true, }); }); }); @@ -163,7 +154,7 @@ describe('Server', () => { await lastValueFrom( server.transformToObservable(Promise.resolve(value)), ), - ).to.be.eq(100); + ).toBe(100); }); }); describe('is Observable', () => { @@ -171,23 +162,23 @@ describe('Server', () => { const value = 100; expect( await lastValueFrom(server.transformToObservable(of(value))), - ).to.be.eq(100); + ).toBe(100); }); }); describe('is any number', () => { it('should return Observable that emits the supplied number', async () => { const value = 100; - expect( - await lastValueFrom(server.transformToObservable(value)), - ).to.be.eq(100); + expect(await lastValueFrom(server.transformToObservable(value))).toBe( + 100, + ); }); }); describe('is an array', () => { it('should return Observable that emits the supplied array', async () => { const value = [1, 2, 3]; - expect( - await lastValueFrom(server.transformToObservable(value)), - ).to.be.eq(value); + expect(await lastValueFrom(server.transformToObservable(value))).toBe( + value, + ); }); }); }); @@ -196,55 +187,62 @@ describe('Server', () => { describe('getHandlers', () => { it('should return registered handlers', () => { const messageHandlers = [() => null, () => true]; - sandbox.stub(server as any, 'messageHandlers').value(messageHandlers); - expect(server.getHandlers()).to.equal(messageHandlers); + const original = untypedServer.messageHandlers; + untypedServer.messageHandlers = messageHandlers; + expect(server.getHandlers()).toBe(messageHandlers); + untypedServer.messageHandlers = original; }); }); describe('getHandlerByPattern', () => { - let messageHandlersGetSpy: sinon.SinonStub; - let messageHandlersHasSpy: sinon.SinonStub; + let messageHandlersGetSpy: ReturnType; + let messageHandlersHasSpy: ReturnType; + let originalMessageHandlers: any; const handlerRoute = 'hello'; beforeEach(() => { - sandbox - .stub(server as any, 'messageHandlers') - .value({ get() {}, has() {} }); - messageHandlersGetSpy = sinon - .stub(untypedServer.messageHandlers, 'get') - .returns(callback); - messageHandlersHasSpy = sinon.stub(untypedServer.messageHandlers, 'has'); - - sandbox.stub(server as any, 'getRouteFromPattern').returns(handlerRoute); + originalMessageHandlers = untypedServer.messageHandlers; + untypedServer.messageHandlers = new Map(); + messageHandlersGetSpy = vi + .spyOn(untypedServer.messageHandlers, 'get') + .mockReturnValue(callback); + messageHandlersHasSpy = vi + .spyOn(untypedServer.messageHandlers, 'has') + .mockReturnValue(false); + + vi.spyOn(server as any, 'getRouteFromPattern').mockReturnValue( + handlerRoute, + ); }); afterEach(() => { - messageHandlersGetSpy.restore(); - messageHandlersHasSpy.restore(); + messageHandlersGetSpy.mockRestore(); + messageHandlersHasSpy.mockRestore(); + untypedServer.messageHandlers = originalMessageHandlers; }); describe('when handler exists', () => { it('should return expected handler', () => { - messageHandlersHasSpy.returns(true); + messageHandlersHasSpy.mockReturnValue(true); const value = server.getHandlerByPattern(handlerRoute); - expect(messageHandlersHasSpy.args[0][0]).to.be.equal(handlerRoute); - expect(messageHandlersGetSpy.called).to.be.true; - expect(messageHandlersGetSpy.args[0][0]).to.be.equal(handlerRoute); - expect(value).to.be.equal(callback); + expect(messageHandlersHasSpy.mock.calls[0][0]).toBe(handlerRoute); + expect(messageHandlersGetSpy).toHaveBeenCalled(); + expect(messageHandlersGetSpy.mock.calls[0][0]).toBe(handlerRoute); + expect(value).toBe(callback); }); }); describe('when handler does not exists', () => { it('should return null', () => { - messageHandlersHasSpy.returns(false); + messageHandlersHasSpy.mockReturnValue(false); const value = server.getHandlerByPattern(handlerRoute); - expect(messageHandlersHasSpy.args[0][0]).to.be.equal(handlerRoute); - expect(messageHandlersGetSpy.called).to.be.false; - expect(value).to.be.null; + expect(messageHandlersHasSpy.mock.calls[0][0]).toBe(handlerRoute); + expect(messageHandlersGetSpy).not.toHaveBeenCalled(); + expect(value).toBeNull(); }); }); }); diff --git a/packages/microservices/test/server/utils/object-to-map.ts b/packages/microservices/test/server/utils/object-to-map.ts index a6a383f60db..7be0a383a08 100644 --- a/packages/microservices/test/server/utils/object-to-map.ts +++ b/packages/microservices/test/server/utils/object-to-map.ts @@ -1,2 +1,2 @@ export const objectToMap = (obj: Record) => - new Map(Object.keys(obj).map(key => [key, obj[key]]) as any); + new Map(Object.entries(obj)); diff --git a/packages/microservices/test/utils/transform-pattern.utils.spec.ts b/packages/microservices/test/utils/transform-pattern.utils.spec.ts index 4ea4b842374..9cc0a2ff816 100644 --- a/packages/microservices/test/utils/transform-pattern.utils.spec.ts +++ b/packages/microservices/test/utils/transform-pattern.utils.spec.ts @@ -1,11 +1,10 @@ -import { expect } from 'chai'; -import { MsPattern } from '../../interfaces'; -import { transformPatternToRoute } from '../../utils/transform-pattern.utils'; +import { MsPattern } from '../../interfaces/index.js'; +import { transformPatternToRoute } from '../../utils/transform-pattern.utils.js'; function equalTest(testPatterns: MsPattern[], expectedResults: R[]) { testPatterns.forEach((testPattern: MsPattern, index: number) => { const testData = transformPatternToRoute(testPattern); - expect(testData).to.be.equal(expectedResults[index]); + expect(testData).toBe(expectedResults[index]); }); } describe('transformPatternToRoute', () => { @@ -116,7 +115,7 @@ describe('transformPatternToRoute', () => { const testPatterns = [null, undefined, Symbol(213)]; testPatterns.forEach((testPattern: any) => { - expect(transformPatternToRoute(testPattern)).to.be.eq(testPattern); + expect(transformPatternToRoute(testPattern)).toBe(testPattern); }); }); }); diff --git a/packages/microservices/tsconfig.build.json b/packages/microservices/tsconfig.build.json index e8b71a07aeb..38ded6e68ff 100644 --- a/packages/microservices/tsconfig.build.json +++ b/packages/microservices/tsconfig.build.json @@ -2,15 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/core": ["../core"], - "@nestjs/core/*": ["../core/*"], - "@nestjs/websockets": ["../websockets"], - "@nestjs/websockets/*": ["../websockets/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/microservices/utils/index.ts b/packages/microservices/utils/index.ts index 74dcc28e97c..03896079704 100644 --- a/packages/microservices/utils/index.ts +++ b/packages/microservices/utils/index.ts @@ -1 +1 @@ -export * from './transform-pattern.utils'; +export * from './transform-pattern.utils.js'; diff --git a/packages/microservices/utils/param.utils.ts b/packages/microservices/utils/param.utils.ts index 6096709dbbe..dc7344bc78d 100644 --- a/packages/microservices/utils/param.utils.ts +++ b/packages/microservices/utils/param.utils.ts @@ -1,9 +1,13 @@ -import { PipeTransform, Type } from '@nestjs/common'; -import { assignMetadata } from '@nestjs/common/decorators/http/route-params.decorator'; -import { isNil, isString } from '@nestjs/common/utils/shared.utils'; +import type { + ParameterDecoratorOptions, + PipeTransform, + Type, +} from '@nestjs/common'; +import { assignMetadata } from '@nestjs/common'; +import { isNil, isString } from '@nestjs/common/internal'; import 'reflect-metadata'; -import { PARAM_ARGS_METADATA } from '../constants'; -import { RpcParamtype } from '../enums/rpc-paramtype.enum'; +import { PARAM_ARGS_METADATA } from '../constants.js'; +import { RpcParamtype } from '../enums/rpc-paramtype.enum.js'; export function createRpcParamDecorator( paramtype: RpcParamtype, @@ -15,7 +19,7 @@ export function createRpcParamDecorator( {}; Reflect.defineMetadata( PARAM_ARGS_METADATA, - assignMetadata(args, paramtype, index, undefined, ...pipes), + assignMetadata(args, paramtype, index, { pipes }), target.constructor, key!, ); @@ -26,19 +30,66 @@ export const createPipesRpcParamDecorator = (paramtype: RpcParamtype) => ( data?: any, + optionsOrPipe?: + | ParameterDecoratorOptions + | Type + | PipeTransform, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator => (target, key, index) => { const args = Reflect.getMetadata(PARAM_ARGS_METADATA, target.constructor, key!) || {}; + const isDataOptions = + data && + typeof data === 'object' && + !('transform' in data) && + ('schema' in data || 'pipes' in data); + + if (isDataOptions) { + const opts = data as ParameterDecoratorOptions; + Reflect.defineMetadata( + PARAM_ARGS_METADATA, + assignMetadata(args, paramtype, index, { + pipes: opts.pipes ?? [], + schema: opts.schema, + }), + target.constructor, + key!, + ); + return; + } + const hasParamData = isNil(data) || isString(data); const paramData = hasParamData ? data : undefined; - const paramPipes = hasParamData ? pipes : [data, ...pipes]; + + const isOptions = + optionsOrPipe && + typeof optionsOrPipe === 'object' && + ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe); + + let paramPipes: (Type | PipeTransform)[]; + if (isOptions) { + paramPipes = optionsOrPipe.pipes ?? []; + } else if (hasParamData) { + paramPipes = [optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]; + } else { + paramPipes = [data, optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]; + } Reflect.defineMetadata( PARAM_ARGS_METADATA, - assignMetadata(args, paramtype, index, paramData!, ...paramPipes), + assignMetadata(args, paramtype, index, { + data: paramData!, + pipes: paramPipes, + schema: isOptions ? optionsOrPipe.schema : undefined, + }), target.constructor, key!, ); diff --git a/packages/microservices/utils/transform-pattern.utils.ts b/packages/microservices/utils/transform-pattern.utils.ts index 4bcb299edfc..a0010578e40 100644 --- a/packages/microservices/utils/transform-pattern.utils.ts +++ b/packages/microservices/utils/transform-pattern.utils.ts @@ -1,9 +1,5 @@ -import { - isObject, - isString, - isNumber, -} from '@nestjs/common/utils/shared.utils'; -import { MsPattern } from '../interfaces'; +import { MsPattern } from '../interfaces/index.js'; +import { isObject, isString, isNumber } from '@nestjs/common/internal'; /** * Transforms the Pattern to Route. diff --git a/packages/platform-express/adapters/express-adapter.ts b/packages/platform-express/adapters/express-adapter.ts index 454396ba076..c660570d13c 100644 --- a/packages/platform-express/adapters/express-adapter.ts +++ b/packages/platform-express/adapters/express-adapter.ts @@ -1,40 +1,41 @@ import { + BadRequestException, HttpStatus, InternalServerErrorException, Logger, - RequestMethod, + type RequestMethod, StreamableFile, VERSION_NEUTRAL, - VersioningOptions, + type VersioningOptions, VersioningType, } from '@nestjs/common'; -import { VersionValue } from '@nestjs/common/interfaces'; -import { - CorsOptions, - CorsOptionsDelegate, -} from '@nestjs/common/interfaces/external/cors-options.interface'; -import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; +import cors from 'cors'; +import express from 'express'; +import type { Server } from 'http'; +import * as http from 'http'; +import * as https from 'https'; +import { pathToRegexp } from 'path-to-regexp'; +import { Duplex, Writable } from 'stream'; +import { NestExpressBodyParserOptions } from '../interfaces/nest-express-body-parser-options.interface.js'; +import { NestExpressBodyParserType } from '../interfaces/nest-express-body-parser.interface.js'; +import { ServeStaticOptions } from '../interfaces/serve-static-options.interface.js'; +import { getBodyParserOptions } from './utils/get-body-parser-options.util.js'; import { + type CorsOptions, + type CorsOptionsDelegate, + type VersionValue, isFunction, isNil, isObject, isString, isUndefined, -} from '@nestjs/common/utils/shared.utils'; -import { AbstractHttpAdapter } from '@nestjs/core/adapters/http-adapter'; -import { RouterMethodFactory } from '@nestjs/core/helpers/router-method-factory'; -import { LegacyRouteConverter } from '@nestjs/core/router/legacy-route-converter'; -import * as cors from 'cors'; -import * as express from 'express'; -import type { Server } from 'http'; -import * as http from 'http'; -import * as https from 'https'; -import { pathToRegexp } from 'path-to-regexp'; -import { Duplex, Writable } from 'stream'; -import { NestExpressBodyParserOptions } from '../interfaces/nest-express-body-parser-options.interface'; -import { NestExpressBodyParserType } from '../interfaces/nest-express-body-parser.interface'; -import { ServeStaticOptions } from '../interfaces/serve-static-options.interface'; -import { getBodyParserOptions } from './utils/get-body-parser-options.util'; +} from '@nestjs/common/internal'; +import type { NestApplicationOptions } from '@nestjs/common'; +import { AbstractHttpAdapter } from '@nestjs/core'; +import { + RouterMethodFactory, + LegacyRouteConverter, +} from '@nestjs/core/internal'; type VersionedRoute = < TRequest extends Record = any, @@ -54,6 +55,8 @@ export class ExpressAdapter extends AbstractHttpAdapter< private readonly routerMethodFactory = new RouterMethodFactory(); private readonly logger = new Logger(ExpressAdapter.name); private readonly openConnections = new Set(); + private readonly registeredPrefixes = new Set(); + private isShuttingDown = false; private onRequestHook?: ( req: express.Request, res: express.Response, @@ -166,11 +169,39 @@ export class ExpressAdapter extends AbstractHttpAdapter< } public setErrorHandler(handler: Function, prefix?: string) { + if (prefix) { + const router = express.Router(); + router.use(handler as any); + return this.use(prefix, router); + } return this.use(handler); } public setNotFoundHandler(handler: Function, prefix?: string) { - return this.use(handler); + if (prefix) { + this.registeredPrefixes.add(prefix); + const router = express.Router(); + router.all('*path', handler as any); + return this.use(prefix, router); + } + return this.use( + ( + req: express.Request, + res: express.Response, + next: express.NextFunction, + ) => { + // When multiple apps share this adapter, a non-prefixed app's 404 + // handler may be registered before a prefixed app's routes. Skip + // requests whose path belongs to another app's prefix so they can + // reach the correct route handlers further in the stack. + for (const registeredPrefix of this.registeredPrefixes) { + if (req.originalUrl.startsWith(registeredPrefix)) { + return next(); + } + } + return (handler as any)(req, res, next); + }, + ); } public isHeadersSent(response: any): boolean { @@ -213,7 +244,12 @@ export class ExpressAdapter extends AbstractHttpAdapter< return this.httpServer.listen(port, ...args); } + public beforeClose() { + this.isShuttingDown = true; + } + public close() { + this.isShuttingDown = true; this.closeOpenConnections(); if (!this.httpServer) { @@ -298,6 +334,17 @@ export class ExpressAdapter extends AbstractHttpAdapter< this.httpServer = http.createServer(this.getInstance()); } + if (options?.return503OnClosing) { + this.instance.use((req: any, res: any, next: any) => { + if (this.isShuttingDown) { + res.set('Connection', 'close'); + res.status(503).send('Service Unavailable'); + } else { + next(); + } + }); + } + if (options?.forceCloseConnections) { this.trackOpenConnections(); } @@ -489,6 +536,18 @@ export class ExpressAdapter extends AbstractHttpAdapter< throw new Error('Unsupported versioning options'); } + public mapException(error: unknown): unknown { + switch (true) { + // SyntaxError is thrown by Express body-parser when given invalid JSON (#422, #430) + // URIError is thrown by Express when given a path parameter with an invalid percentage + // encoding, e.g. '%FF' (#8915) + case error instanceof SyntaxError || error instanceof URIError: + return new BadRequestException(error.message); + default: + return error; + } + } + private trackOpenConnections() { this.httpServer.on('connection', (socket: Duplex) => { this.openConnections.add(socket); diff --git a/packages/platform-express/adapters/index.ts b/packages/platform-express/adapters/index.ts index 3644487c623..7591ddf554a 100644 --- a/packages/platform-express/adapters/index.ts +++ b/packages/platform-express/adapters/index.ts @@ -1 +1 @@ -export * from './express-adapter'; +export * from './express-adapter.js'; diff --git a/packages/platform-express/adapters/utils/get-body-parser-options.util.ts b/packages/platform-express/adapters/utils/get-body-parser-options.util.ts index f7833ad68be..90856d6a16a 100644 --- a/packages/platform-express/adapters/utils/get-body-parser-options.util.ts +++ b/packages/platform-express/adapters/utils/get-body-parser-options.util.ts @@ -1,6 +1,6 @@ import type { RawBodyRequest } from '@nestjs/common'; import type { IncomingMessage, ServerResponse } from 'http'; -import type { NestExpressBodyParserOptions } from '../../interfaces'; +import type { NestExpressBodyParserOptions } from '../../interfaces/index.js'; const rawBodyParser = ( req: RawBodyRequest, diff --git a/packages/platform-express/index.ts b/packages/platform-express/index.ts index 9afc7886926..6cc7211a50d 100644 --- a/packages/platform-express/index.ts +++ b/packages/platform-express/index.ts @@ -5,6 +5,6 @@ * MIT Licensed */ -export * from './adapters'; -export * from './interfaces'; -export * from './multer'; +export * from './adapters/index.js'; +export * from './interfaces/index.js'; +export * from './multer/index.js'; diff --git a/packages/platform-express/interfaces/index.ts b/packages/platform-express/interfaces/index.ts index ac057435a09..8b0c441976c 100644 --- a/packages/platform-express/interfaces/index.ts +++ b/packages/platform-express/interfaces/index.ts @@ -1,3 +1,3 @@ -export * from './nest-express-application.interface'; -export { NestExpressBodyParserOptions } from './nest-express-body-parser-options.interface'; -export * from './nest-express-body-parser.interface'; +export * from './nest-express-application.interface.js'; +export { NestExpressBodyParserOptions } from './nest-express-body-parser-options.interface.js'; +export * from './nest-express-body-parser.interface.js'; diff --git a/packages/platform-express/interfaces/nest-express-application.interface.ts b/packages/platform-express/interfaces/nest-express-application.interface.ts index ade7aecf00c..e4f883f86af 100644 --- a/packages/platform-express/interfaces/nest-express-application.interface.ts +++ b/packages/platform-express/interfaces/nest-express-application.interface.ts @@ -1,14 +1,11 @@ -import { HttpServer, INestApplication } from '@nestjs/common'; -import type { - CorsOptions, - CorsOptionsDelegate, -} from '@nestjs/common/interfaces/external/cors-options.interface'; +import type { HttpServer, INestApplication } from '@nestjs/common'; import type { Express } from 'express'; import type { Server as CoreHttpServer } from 'http'; import type { Server as CoreHttpsServer } from 'https'; -import { NestExpressBodyParserOptions } from './nest-express-body-parser-options.interface'; -import { NestExpressBodyParserType } from './nest-express-body-parser.interface'; -import { ServeStaticOptions } from './serve-static-options.interface'; +import { NestExpressBodyParserOptions } from './nest-express-body-parser-options.interface.js'; +import { NestExpressBodyParserType } from './nest-express-body-parser.interface.js'; +import { ServeStaticOptions } from './serve-static-options.interface.js'; +import type { CorsOptions, CorsOptionsDelegate } from '@nestjs/common/internal'; /** * Interface describing methods on NestExpressApplication. diff --git a/packages/platform-express/multer/index.ts b/packages/platform-express/multer/index.ts index 7afd4aab899..ed06a2e569f 100644 --- a/packages/platform-express/multer/index.ts +++ b/packages/platform-express/multer/index.ts @@ -1,3 +1,3 @@ -export * from './interceptors'; -export * from './interfaces'; -export * from './multer.module'; +export * from './interceptors/index.js'; +export * from './interfaces/index.js'; +export * from './multer.module.js'; diff --git a/packages/platform-express/multer/interceptors/any-files.interceptor.ts b/packages/platform-express/multer/interceptors/any-files.interceptor.ts index 28a332fdd4e..c840ffc1044 100644 --- a/packages/platform-express/multer/interceptors/any-files.interceptor.ts +++ b/packages/platform-express/multer/interceptors/any-files.interceptor.ts @@ -1,18 +1,18 @@ import { - CallHandler, - ExecutionContext, + type CallHandler, + type ExecutionContext, Inject, mixin, - NestInterceptor, + type NestInterceptor, Optional, - Type, + type Type, } from '@nestjs/common'; -import * as multer from 'multer'; +import multer from 'multer'; import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; -import { MulterOptions } from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +import { MULTER_MODULE_OPTIONS } from '../files.constants.js'; +import { MulterModuleOptions } from '../interfaces/index.js'; +import { MulterOptions } from '../interfaces/multer-options.interface.js'; +import { transformException } from '../multer/multer.utils.js'; type MulterInstance = any; diff --git a/packages/platform-express/multer/interceptors/file-fields.interceptor.ts b/packages/platform-express/multer/interceptors/file-fields.interceptor.ts index 27f05afca70..17e2bd95d75 100644 --- a/packages/platform-express/multer/interceptors/file-fields.interceptor.ts +++ b/packages/platform-express/multer/interceptors/file-fields.interceptor.ts @@ -1,21 +1,21 @@ import { - CallHandler, - ExecutionContext, + type CallHandler, + type ExecutionContext, Inject, mixin, - NestInterceptor, + type NestInterceptor, Optional, - Type, + type Type, } from '@nestjs/common'; -import * as multer from 'multer'; +import multer from 'multer'; import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; +import { MULTER_MODULE_OPTIONS } from '../files.constants.js'; +import { MulterModuleOptions } from '../interfaces/index.js'; import { MulterField, MulterOptions, -} from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +} from '../interfaces/multer-options.interface.js'; +import { transformException } from '../multer/multer.utils.js'; type MulterInstance = any; diff --git a/packages/platform-express/multer/interceptors/file.interceptor.ts b/packages/platform-express/multer/interceptors/file.interceptor.ts index bbc8f3947ff..6ecfed7d619 100644 --- a/packages/platform-express/multer/interceptors/file.interceptor.ts +++ b/packages/platform-express/multer/interceptors/file.interceptor.ts @@ -1,18 +1,18 @@ import { - CallHandler, - ExecutionContext, + type CallHandler, + type ExecutionContext, Inject, mixin, - NestInterceptor, + type NestInterceptor, Optional, - Type, + type Type, } from '@nestjs/common'; -import * as multer from 'multer'; +import multer from 'multer'; import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; -import { MulterOptions } from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +import { MULTER_MODULE_OPTIONS } from '../files.constants.js'; +import { MulterModuleOptions } from '../interfaces/index.js'; +import { MulterOptions } from '../interfaces/multer-options.interface.js'; +import { transformException } from '../multer/multer.utils.js'; type MulterInstance = any; diff --git a/packages/platform-express/multer/interceptors/files.interceptor.ts b/packages/platform-express/multer/interceptors/files.interceptor.ts index dbdb1abe0fe..54b424a5da4 100644 --- a/packages/platform-express/multer/interceptors/files.interceptor.ts +++ b/packages/platform-express/multer/interceptors/files.interceptor.ts @@ -1,18 +1,18 @@ import { - CallHandler, - ExecutionContext, + type CallHandler, + type ExecutionContext, Inject, mixin, - NestInterceptor, + type NestInterceptor, Optional, - Type, + type Type, } from '@nestjs/common'; -import * as multer from 'multer'; +import multer from 'multer'; import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; -import { MulterOptions } from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +import { MULTER_MODULE_OPTIONS } from '../files.constants.js'; +import { MulterModuleOptions } from '../interfaces/index.js'; +import { MulterOptions } from '../interfaces/multer-options.interface.js'; +import { transformException } from '../multer/multer.utils.js'; type MulterInstance = any; diff --git a/packages/platform-express/multer/interceptors/index.ts b/packages/platform-express/multer/interceptors/index.ts index 855620d9762..5bdca244761 100644 --- a/packages/platform-express/multer/interceptors/index.ts +++ b/packages/platform-express/multer/interceptors/index.ts @@ -1,5 +1,5 @@ -export * from './any-files.interceptor'; -export * from './file-fields.interceptor'; -export * from './file.interceptor'; -export * from './files.interceptor'; -export * from './no-files.interceptor'; +export * from './any-files.interceptor.js'; +export * from './file-fields.interceptor.js'; +export * from './file.interceptor.js'; +export * from './files.interceptor.js'; +export * from './no-files.interceptor.js'; diff --git a/packages/platform-express/multer/interceptors/no-files.interceptor.ts b/packages/platform-express/multer/interceptors/no-files.interceptor.ts index 696df1ab319..e643858b0e6 100644 --- a/packages/platform-express/multer/interceptors/no-files.interceptor.ts +++ b/packages/platform-express/multer/interceptors/no-files.interceptor.ts @@ -1,18 +1,18 @@ import { - CallHandler, - ExecutionContext, + type CallHandler, + type ExecutionContext, Inject, mixin, - NestInterceptor, + type NestInterceptor, Optional, - Type, + type Type, } from '@nestjs/common'; -import * as multer from 'multer'; +import multer from 'multer'; import { Observable } from 'rxjs'; -import { MULTER_MODULE_OPTIONS } from '../files.constants'; -import { MulterModuleOptions } from '../interfaces'; -import { MulterOptions } from '../interfaces/multer-options.interface'; -import { transformException } from '../multer/multer.utils'; +import { MULTER_MODULE_OPTIONS } from '../files.constants.js'; +import { MulterModuleOptions } from '../interfaces/index.js'; +import { MulterOptions } from '../interfaces/multer-options.interface.js'; +import { transformException } from '../multer/multer.utils.js'; type MulterInstance = any; diff --git a/packages/platform-express/multer/interfaces/files-upload-module.interface.ts b/packages/platform-express/multer/interfaces/files-upload-module.interface.ts index 46623cc999a..3f6cbd27522 100644 --- a/packages/platform-express/multer/interfaces/files-upload-module.interface.ts +++ b/packages/platform-express/multer/interfaces/files-upload-module.interface.ts @@ -1,5 +1,5 @@ -import { ModuleMetadata, Type } from '@nestjs/common/interfaces'; -import { MulterOptions } from './multer-options.interface'; +import { MulterOptions } from './multer-options.interface.js'; +import type { ModuleMetadata, Type } from '@nestjs/common'; export type MulterModuleOptions = MulterOptions; diff --git a/packages/platform-express/multer/interfaces/index.ts b/packages/platform-express/multer/interfaces/index.ts index be94ec24afd..1e6d82337b6 100644 --- a/packages/platform-express/multer/interfaces/index.ts +++ b/packages/platform-express/multer/interfaces/index.ts @@ -1 +1 @@ -export * from './files-upload-module.interface'; +export * from './files-upload-module.interface.js'; diff --git a/packages/platform-express/multer/multer.module.ts b/packages/platform-express/multer/multer.module.ts index 0ee770b9305..33fadb90dec 100644 --- a/packages/platform-express/multer/multer.module.ts +++ b/packages/platform-express/multer/multer.module.ts @@ -1,12 +1,12 @@ -import { DynamicModule, Module, Provider } from '@nestjs/common'; -import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util'; -import { MULTER_MODULE_OPTIONS } from './files.constants'; +import { type DynamicModule, Module, type Provider } from '@nestjs/common'; +import { MULTER_MODULE_OPTIONS } from './files.constants.js'; import { MulterModuleAsyncOptions, MulterModuleOptions, MulterOptionsFactory, -} from './interfaces/files-upload-module.interface'; -import { MULTER_MODULE_ID } from './multer.constants'; +} from './interfaces/files-upload-module.interface.js'; +import { MULTER_MODULE_ID } from './multer.constants.js'; +import { randomStringGenerator } from '@nestjs/common/internal'; /** * @publicApi diff --git a/packages/platform-express/multer/multer/multer.utils.ts b/packages/platform-express/multer/multer/multer.utils.ts index 62c1a7f1074..c5e13295239 100644 --- a/packages/platform-express/multer/multer/multer.utils.ts +++ b/packages/platform-express/multer/multer/multer.utils.ts @@ -3,7 +3,7 @@ import { HttpException, PayloadTooLargeException, } from '@nestjs/common'; -import { multerExceptions, busboyExceptions } from './multer.constants'; +import { multerExceptions, busboyExceptions } from './multer.constants.js'; // Multer may add in a 'field' property to the error // https://github.com/expressjs/multer/blob/aa42bea6ac7d0cb8fcb279b15a7278cda805dc63/lib/multer-error.js#L19 diff --git a/packages/platform-express/package.json b/packages/platform-express/package.json index 091346d2e48..db0e4bc5afe 100644 --- a/packages/platform-express/package.json +++ b/packages/platform-express/package.json @@ -4,6 +4,13 @@ "description": "Nest - modern, fast, powerful node.js web framework (@platform-express)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "homepage": "https://nestjs.com", "funding": { "type": "opencollective", diff --git a/packages/platform-express/test/adapters/express-adapter.spec.ts b/packages/platform-express/test/adapters/express-adapter.spec.ts index 622b9e7aee9..600825ec160 100644 --- a/packages/platform-express/test/adapters/express-adapter.spec.ts +++ b/packages/platform-express/test/adapters/express-adapter.spec.ts @@ -1,48 +1,70 @@ +import { BadRequestException } from '@nestjs/common'; import { ExpressAdapter } from '@nestjs/platform-express'; -import { expect } from 'chai'; -import * as express from 'express'; -import * as sinon from 'sinon'; +import express from 'express'; describe('ExpressAdapter', () => { - afterEach(() => sinon.restore()); + afterEach(() => vi.restoreAllMocks()); + let expressAdapter: ExpressAdapter; + + beforeEach(() => { + expressAdapter = new ExpressAdapter(); + }); describe('registerParserMiddleware', () => { it('should register the express built-in parsers for json and urlencoded payloads', () => { const expressInstance = express(); const jsonParserInstance = express.json(); const urlencodedInstance = express.urlencoded(); - const jsonParserSpy = sinon - .stub(express, 'json') - .returns(jsonParserInstance); - const urlencodedParserSpy = sinon - .stub(express, 'urlencoded') - .returns(urlencodedInstance); - const useSpy = sinon.spy(expressInstance, 'use'); + const jsonParserSpy = vi + .spyOn(express, 'json') + .mockReturnValue(jsonParserInstance as any); + const urlencodedParserSpy = vi + .spyOn(express, 'urlencoded') + .mockReturnValue(urlencodedInstance as any); + const useSpy = vi.spyOn(expressInstance, 'use'); const expressAdapter = new ExpressAdapter(expressInstance); - useSpy.resetHistory(); + useSpy.mockClear(); expressAdapter.registerParserMiddleware(); - expect(useSpy.calledTwice).to.be.true; - expect(useSpy.calledWith(sinon.match.same(jsonParserInstance))).to.be - .true; - expect(useSpy.calledWith(sinon.match.same(urlencodedInstance))).to.be - .true; - expect(jsonParserSpy.calledOnceWith({})).to.be.true; - expect(urlencodedParserSpy.calledOnceWith({ extended: true })).to.be.true; + expect(useSpy).toHaveBeenCalledTimes(2); + expect(useSpy).toHaveBeenCalledWith(jsonParserInstance); + expect(useSpy).toHaveBeenCalledWith(urlencodedInstance); + expect(jsonParserSpy).toHaveBeenCalledWith({}); + expect(urlencodedParserSpy).toHaveBeenCalledWith({ extended: true }); }); it('should not register default parsers if custom parsers have already been registered', () => { const expressInstance = express(); expressInstance.use(function jsonParser() {}); expressInstance.use(function urlencodedParser() {}); - const useSpy = sinon.spy(expressInstance, 'use'); + const useSpy = vi.spyOn(expressInstance, 'use'); const expressAdapter = new ExpressAdapter(expressInstance); - useSpy.resetHistory(); + useSpy.mockClear(); expressAdapter.registerParserMiddleware(); - expect(useSpy.called).to.be.false; + expect(useSpy).not.toHaveBeenCalled(); + }); + }); + + describe('mapException', () => { + it('should map URIError with status code to BadRequestException', () => { + const error = new URIError(); + const result = expressAdapter.mapException(error) as BadRequestException; + expect(result).to.be.instanceOf(BadRequestException); + }); + + it('should map SyntaxError with status code to BadRequestException', () => { + const error = new SyntaxError(); + const result = expressAdapter.mapException(error) as BadRequestException; + expect(result).to.be.instanceOf(BadRequestException); + }); + + it('should return error if it is not handler Error', () => { + const error = new Error('Test error'); + const result = expressAdapter.mapException(error); + expect(result).to.equal(error); }); }); }); diff --git a/packages/platform-express/test/adapters/utils/get-body-parser-options.util.spec.ts b/packages/platform-express/test/adapters/utils/get-body-parser-options.util.spec.ts index 3a9b8f98f64..888ba466284 100644 --- a/packages/platform-express/test/adapters/utils/get-body-parser-options.util.spec.ts +++ b/packages/platform-express/test/adapters/utils/get-body-parser-options.util.spec.ts @@ -1,37 +1,36 @@ -import { expect } from 'chai'; -import { getBodyParserOptions } from '../../../adapters/utils/get-body-parser-options.util'; +import { getBodyParserOptions } from '../../../adapters/utils/get-body-parser-options.util.js'; describe('getBodyParserOptions', () => { describe('when rawBody is false', () => { it('should return empty options when no options provided', () => { const result = getBodyParserOptions(false); - expect(result).to.deep.equal({}); + expect(result).toEqual({}); }); it('should return provided options unchanged', () => { const options = { limit: '10mb', inflate: true }; const result = getBodyParserOptions(false, options); - expect(result).to.deep.equal(options); + expect(result).toEqual(options); }); it('should not include verify function', () => { const result = getBodyParserOptions(false); - expect(result).to.not.have.property('verify'); + expect(result).not.toHaveProperty('verify'); }); }); describe('when rawBody is true', () => { it('should include verify function in options', () => { const result = getBodyParserOptions(true); - expect(result).to.have.property('verify'); - expect(result.verify).to.be.a('function'); + expect(result).toHaveProperty('verify'); + expect(result.verify).toBeTypeOf('function'); }); it('should merge verify with existing options', () => { const options = { limit: '10mb' }; const result = getBodyParserOptions(true, options); - expect(result.limit).to.equal('10mb'); - expect(result.verify).to.be.a('function'); + expect(result.limit).toBe('10mb'); + expect(result.verify).toBeTypeOf('function'); }); describe('verify function (rawBodyParser)', () => { @@ -43,8 +42,8 @@ describe('getBodyParserOptions', () => { const returnValue = result.verify(req, res, buffer); - expect(req.rawBody).to.equal(buffer); - expect(returnValue).to.be.true; + expect(req.rawBody).toBe(buffer); + expect(returnValue).toBe(true); }); it('should not assign rawBody when buffer is not a Buffer', () => { @@ -55,8 +54,8 @@ describe('getBodyParserOptions', () => { const returnValue = result.verify(req, res, notBuffer); - expect(req.rawBody).to.be.undefined; - expect(returnValue).to.be.true; + expect(req.rawBody).toBeUndefined(); + expect(returnValue).toBe(true); }); it('should always return true', () => { @@ -64,8 +63,8 @@ describe('getBodyParserOptions', () => { const req: any = {}; const res: any = {}; - expect(result.verify(req, res, Buffer.from(''))).to.be.true; - expect(result.verify(req, res, null)).to.be.true; + expect(result.verify(req, res, Buffer.from(''))).toBe(true); + expect(result.verify(req, res, null)).toBe(true); }); }); }); diff --git a/packages/platform-express/test/multer/interceptors/any-files.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/any-files.interceptor.spec.ts index 796d389920a..e5728902dc8 100644 --- a/packages/platform-express/test/multer/interceptors/any-files.interceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/any-files.interceptor.spec.ts @@ -1,14 +1,12 @@ import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { AnyFilesInterceptor } from '../../../multer/interceptors/any-files.interceptor'; +import { AnyFilesInterceptor } from '../../../multer/interceptors/any-files.interceptor.js'; describe('FilesInterceptor', () => { it('should return metatype with expected structure', async () => { const targetClass = AnyFilesInterceptor(); - expect(targetClass.prototype.intercept).to.not.be.undefined; + expect(targetClass.prototype.intercept).not.toBeUndefined(); }); describe('intercept', () => { let handler: CallHandler; @@ -21,14 +19,14 @@ describe('FilesInterceptor', () => { const target = new (AnyFilesInterceptor())(); const callback = (req, res, next) => next(); - const arraySpy = sinon - .stub((target as any).multer, 'any') - .returns(callback); + const arraySpy = vi + .spyOn((target as any).multer, 'any') + .mockReturnValue(callback); await target.intercept(new ExecutionContextHost([]), handler); - expect(arraySpy.called).to.be.true; - expect(arraySpy.calledWith()).to.be.true; + expect(arraySpy).toHaveBeenCalled(); + expect(arraySpy).toHaveBeenCalledWith(); }); it('should transform exception', async () => { const target = new (AnyFilesInterceptor())(); @@ -38,7 +36,7 @@ describe('FilesInterceptor', () => { any: () => callback, }; (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, + error => expect(error).not.toBeUndefined(), ); }); }); diff --git a/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts index 2d7fbf2ae7d..58e085ca2ac 100644 --- a/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/file-fields.interceptor.spec.ts @@ -1,9 +1,7 @@ import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { FileFieldsInterceptor } from '../../../multer/interceptors/file-fields.interceptor'; +import { FileFieldsInterceptor } from '../../../multer/interceptors/file-fields.interceptor.js'; describe('FileFieldsInterceptor', () => { it('should return metatype with expected structure', async () => { @@ -11,7 +9,7 @@ describe('FileFieldsInterceptor', () => { { name: 'file', maxCount: 1 }, { name: 'anotherFile', maxCount: 1 }, ]); - expect(targetClass.prototype.intercept).to.not.be.undefined; + expect(targetClass.prototype.intercept).not.toBeUndefined(); }); describe('intercept', () => { let handler: CallHandler; @@ -32,14 +30,14 @@ describe('FileFieldsInterceptor', () => { const target = new (FileFieldsInterceptor(argument))(); const callback = (req, res, next) => next(); - const fieldsSpy = sinon - .stub((target as any).multer, 'fields') - .returns(callback); + const fieldsSpy = vi + .spyOn((target as any).multer, 'fields') + .mockReturnValue(callback); await target.intercept(new ExecutionContextHost([]), handler); - expect(fieldsSpy.called).to.be.true; - expect(fieldsSpy.calledWith(argument)).to.be.true; + expect(fieldsSpy).toHaveBeenCalled(); + expect(fieldsSpy).toHaveBeenCalledWith(argument); }); it('should transform exception', async () => { const fieldName1 = 'file'; @@ -57,7 +55,7 @@ describe('FileFieldsInterceptor', () => { array: () => callback, }; (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, + error => expect(error).not.toBeUndefined(), ); }); }); diff --git a/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts index 39f003e1465..64fed653cf8 100644 --- a/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/file.interceptor.spec.ts @@ -1,14 +1,12 @@ import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { FileInterceptor } from '../../../multer/interceptors/file.interceptor'; +import { FileInterceptor } from '../../../multer/interceptors/file.interceptor.js'; describe('FileInterceptor', () => { it('should return metatype with expected structure', async () => { const targetClass = FileInterceptor('file'); - expect(targetClass.prototype.intercept).to.not.be.undefined; + expect(targetClass.prototype.intercept).not.toBeUndefined(); }); describe('intercept', () => { let handler: CallHandler; @@ -21,14 +19,14 @@ describe('FileInterceptor', () => { const fieldName = 'file'; const target = new (FileInterceptor(fieldName))(); const callback = (req, res, next) => next(); - const singleSpy = sinon - .stub((target as any).multer, 'single') - .returns(callback); + const singleSpy = vi + .spyOn((target as any).multer, 'single') + .mockReturnValue(callback); await target.intercept(new ExecutionContextHost([]), handler); - expect(singleSpy.called).to.be.true; - expect(singleSpy.calledWith(fieldName)).to.be.true; + expect(singleSpy).toHaveBeenCalled(); + expect(singleSpy).toHaveBeenCalledWith(fieldName); }); it('should transform exception', async () => { const fieldName = 'file'; @@ -39,7 +37,7 @@ describe('FileInterceptor', () => { single: () => callback, }; (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, + error => expect(error).not.toBeUndefined(), ); }); }); diff --git a/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts b/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts index 20c6b0803de..de1563d1088 100644 --- a/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/files.interceptor.spec.ts @@ -1,14 +1,12 @@ import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { FilesInterceptor } from '../../../multer/interceptors/files.interceptor'; +import { FilesInterceptor } from '../../../multer/interceptors/files.interceptor.js'; describe('FilesInterceptor', () => { it('should return metatype with expected structure', async () => { const targetClass = FilesInterceptor('file'); - expect(targetClass.prototype.intercept).to.not.be.undefined; + expect(targetClass.prototype.intercept).not.toBeUndefined(); }); describe('intercept', () => { let handler: CallHandler; @@ -23,14 +21,14 @@ describe('FilesInterceptor', () => { const target = new (FilesInterceptor(fieldName, maxCount))(); const callback = (req, res, next) => next(); - const arraySpy = sinon - .stub((target as any).multer, 'array') - .returns(callback); + const arraySpy = vi + .spyOn((target as any).multer, 'array') + .mockReturnValue(callback); await target.intercept(new ExecutionContextHost([]), handler); - expect(arraySpy.called).to.be.true; - expect(arraySpy.calledWith(fieldName, maxCount)).to.be.true; + expect(arraySpy).toHaveBeenCalled(); + expect(arraySpy).toHaveBeenCalledWith(fieldName, maxCount); }); it('should transform exception', async () => { const fieldName = 'file'; @@ -41,7 +39,7 @@ describe('FilesInterceptor', () => { array: () => callback, }; (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, + error => expect(error).not.toBeUndefined(), ); }); }); diff --git a/packages/platform-express/test/multer/interceptors/no-files.inteceptor.spec.ts b/packages/platform-express/test/multer/interceptors/no-files.inteceptor.spec.ts index 5b5289941fa..98465d13a01 100644 --- a/packages/platform-express/test/multer/interceptors/no-files.inteceptor.spec.ts +++ b/packages/platform-express/test/multer/interceptors/no-files.inteceptor.spec.ts @@ -1,14 +1,12 @@ import { CallHandler } from '@nestjs/common'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { NoFilesInterceptor } from '../../../multer/interceptors/no-files.interceptor'; +import { NoFilesInterceptor } from '../../../multer/interceptors/no-files.interceptor.js'; describe('NoFilesInterceptor', () => { it('should return metatype with expected structure', async () => { const targetClass = NoFilesInterceptor(); - expect(targetClass.prototype.intercept).to.not.be.undefined; + expect(targetClass.prototype.intercept).not.toBeUndefined(); }); describe('intercept', () => { let handler: CallHandler; @@ -21,13 +19,13 @@ describe('NoFilesInterceptor', () => { const target = new (NoFilesInterceptor())(); const callback = (req, res, next) => next(); - const noneSpy = sinon - .stub((target as any).multer, 'none') - .returns(callback); + const noneSpy = vi + .spyOn((target as any).multer, 'none') + .mockReturnValue(callback); await target.intercept(new ExecutionContextHost([]), handler); - expect(noneSpy.called).to.be.true; + expect(noneSpy).toHaveBeenCalled(); }); it('should transform exception', async () => { const target = new (NoFilesInterceptor())(); @@ -37,7 +35,7 @@ describe('NoFilesInterceptor', () => { none: () => callback, }; (target.intercept(new ExecutionContextHost([]), handler) as any).catch( - error => expect(error).to.not.be.undefined, + error => expect(error).not.toBeUndefined(), ); }); }); diff --git a/packages/platform-express/test/multer/multer/multer.module.spec.ts b/packages/platform-express/test/multer/multer/multer.module.spec.ts index 63aaf2330b2..ac9f11b175d 100644 --- a/packages/platform-express/test/multer/multer/multer.module.spec.ts +++ b/packages/platform-express/test/multer/multer/multer.module.spec.ts @@ -1,8 +1,6 @@ import { FactoryProvider } from '@nestjs/common'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { MULTER_MODULE_OPTIONS } from '../../../multer/files.constants'; -import { MulterModule } from '../../../multer/multer.module'; +import { MULTER_MODULE_OPTIONS } from '../../../multer/files.constants.js'; +import { MulterModule } from '../../../multer/multer.module.js'; describe('MulterModule', () => { describe('register', () => { @@ -12,15 +10,15 @@ describe('MulterModule', () => { }; const dynamicModule = MulterModule.register(options as any); - expect(dynamicModule.providers).to.have.length(2); - expect(dynamicModule.imports).to.be.undefined; - expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + expect(dynamicModule.providers).toHaveLength(2); + expect(dynamicModule.imports).toBeUndefined(); + expect(dynamicModule.exports).toContain(MULTER_MODULE_OPTIONS); const moduleOptionsProvider = dynamicModule.providers!.find( p => 'useFactory' in p && p.provide === MULTER_MODULE_OPTIONS, ) as FactoryProvider; - expect(moduleOptionsProvider).to.not.be.undefined; - expect(moduleOptionsProvider.useFactory()).to.be.eq(options); + expect(moduleOptionsProvider).not.toBeUndefined(); + expect(moduleOptionsProvider.useFactory()).toBe(options); }); }); @@ -33,14 +31,16 @@ describe('MulterModule', () => { }; const dynamicModule = MulterModule.registerAsync(asyncOptions); - expect(dynamicModule.providers).to.have.length(2); - expect(dynamicModule.imports).to.be.undefined; - expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); - expect(dynamicModule.providers).to.deep.include({ - provide: MULTER_MODULE_OPTIONS, - useFactory: asyncOptions.useFactory, - inject: [], - }); + expect(dynamicModule.providers).toHaveLength(2); + expect(dynamicModule.imports).toBeUndefined(); + expect(dynamicModule.exports).toContain(MULTER_MODULE_OPTIONS); + expect(dynamicModule.providers).toContainEqual( + expect.objectContaining({ + provide: MULTER_MODULE_OPTIONS, + useFactory: asyncOptions.useFactory, + inject: [], + }), + ); }); }); @@ -51,9 +51,9 @@ describe('MulterModule', () => { }; const dynamicModule = MulterModule.registerAsync(asyncOptions as any); - expect(dynamicModule.providers).to.have.length(2); - expect(dynamicModule.imports).to.be.undefined; - expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + expect(dynamicModule.providers).toHaveLength(2); + expect(dynamicModule.imports).toBeUndefined(); + expect(dynamicModule.exports).toContain(MULTER_MODULE_OPTIONS); }); }); @@ -64,9 +64,9 @@ describe('MulterModule', () => { }; const dynamicModule = MulterModule.registerAsync(asyncOptions as any); - expect(dynamicModule.providers).to.have.length(3); - expect(dynamicModule.imports).to.be.undefined; - expect(dynamicModule.exports).to.include(MULTER_MODULE_OPTIONS); + expect(dynamicModule.providers).toHaveLength(3); + expect(dynamicModule.imports).toBeUndefined(); + expect(dynamicModule.exports).toContain(MULTER_MODULE_OPTIONS); }); it('provider should call "createMulterOptions"', async () => { const asyncOptions = { @@ -74,10 +74,10 @@ describe('MulterModule', () => { }; const dynamicModule = MulterModule.registerAsync(asyncOptions as any); const optionsFactory = { - createMulterOptions: sinon.spy(), + createMulterOptions: vi.fn(), }; await (dynamicModule.providers![0] as any).useFactory(optionsFactory); - expect(optionsFactory.createMulterOptions.called).to.be.true; + expect(optionsFactory.createMulterOptions).toHaveBeenCalled(); }); }); }); diff --git a/packages/platform-express/test/multer/multer/multer.utils.spec.ts b/packages/platform-express/test/multer/multer/multer.utils.spec.ts index be409795aaf..3d7760fedb0 100644 --- a/packages/platform-express/test/multer/multer/multer.utils.spec.ts +++ b/packages/platform-express/test/multer/multer/multer.utils.spec.ts @@ -3,31 +3,30 @@ import { HttpException, PayloadTooLargeException, } from '@nestjs/common'; -import { expect } from 'chai'; import { busboyExceptions, multerExceptions, -} from '../../../multer/multer/multer.constants'; -import { transformException } from '../../../multer/multer/multer.utils'; +} from '../../../multer/multer/multer.constants.js'; +import { transformException } from '../../../multer/multer/multer.utils.js'; describe('transformException', () => { describe('if error does not exist', () => { it('should behave as identity', () => { const err = undefined; - expect(transformException(err)).to.be.eq(err); + expect(transformException(err)).toBe(err); }); }); describe('if error is instance of HttpException', () => { it('should behave as identity', () => { const err = new HttpException('response', 500); - expect(transformException(err)).to.be.eq(err); + expect(transformException(err)).toBe(err); }); }); describe('if error exists and is not instance of HttpException', () => { describe('and is LIMIT_FILE_SIZE exception', () => { it('should return "PayloadTooLargeException"', () => { const err = { message: multerExceptions.LIMIT_FILE_SIZE }; - expect(transformException(err as any)).to.be.instanceof( + expect(transformException(err as any)).toBeInstanceOf( PayloadTooLargeException, ); }); @@ -35,7 +34,7 @@ describe('transformException', () => { describe('and is multer exception but not a LIMIT_FILE_SIZE', () => { it('should return "BadRequestException"', () => { const err = { message: multerExceptions.LIMIT_FIELD_KEY }; - expect(transformException(err as any)).to.be.instanceof( + expect(transformException(err as any)).toBeInstanceOf( BadRequestException, ); }); @@ -43,7 +42,7 @@ describe('transformException', () => { describe('and is busboy/multipart exception', () => { it('should return "BadRequestException"', () => { const err = { message: busboyExceptions.MULTIPART_BOUNDARY_NOT_FOUND }; - expect(transformException(err as any)).to.be.instanceof( + expect(transformException(err as any)).toBeInstanceOf( BadRequestException, ); }); @@ -52,7 +51,7 @@ describe('transformException', () => { const err = { message: busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FORM, }; - expect(transformException(err as any)).to.be.instanceof( + expect(transformException(err as any)).toBeInstanceOf( BadRequestException, ); }); @@ -63,7 +62,7 @@ describe('transformException', () => { message: multerExceptions.LIMIT_UNEXPECTED_FILE, field: 'foo', }; - expect(transformException(err as any)!.message).to.equal( + expect(transformException(err as any)!.message).toBe( `${multerExceptions.LIMIT_UNEXPECTED_FILE} - foo`, ); }); diff --git a/packages/platform-express/tsconfig.build.json b/packages/platform-express/tsconfig.build.json index 732ca00cb71..fb0f617fdbe 100644 --- a/packages/platform-express/tsconfig.build.json +++ b/packages/platform-express/tsconfig.build.json @@ -2,13 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/core": ["../core"], - "@nestjs/core/*": ["../core/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/platform-fastify/adapters/fastify-adapter.ts b/packages/platform-fastify/adapters/fastify-adapter.ts index 0831aa5f7d3..ca3a77b4a59 100644 --- a/packages/platform-fastify/adapters/fastify-adapter.ts +++ b/packages/platform-fastify/adapters/fastify-adapter.ts @@ -1,23 +1,21 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { FastifyCorsOptions } from '@fastify/cors'; import { + BadRequestException, + HttpException, HttpStatus, Logger, - RawBodyRequest, - RequestMethod, + type RawBodyRequest, + type RequestMethod, StreamableFile, VERSION_NEUTRAL, - VersioningOptions, + type VersioningOptions, VersioningType, } from '@nestjs/common'; -import { VersionValue } from '@nestjs/common/interfaces'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isString, isUndefined } from '@nestjs/common/utils/shared.utils'; -import { AbstractHttpAdapter } from '@nestjs/core/adapters/http-adapter'; -import { LegacyRouteConverter } from '@nestjs/core/router/legacy-route-converter'; import { FastifyBaseLogger, FastifyBodyParser, + FastifyError, FastifyInstance, FastifyListenOptions, FastifyPluginAsync, @@ -37,8 +35,8 @@ import { RouteShorthandOptions, fastify, } from 'fastify'; -import * as Reply from 'fastify/lib/reply'; -import { kRouteContext } from 'fastify/lib/symbols'; +import * as Reply from 'fastify/lib/reply.js'; +import fastifySymbols from 'fastify/lib/symbols.js'; import * as http from 'http'; import * as http2 from 'http2'; import * as https from 'https'; @@ -48,20 +46,30 @@ import { Response as LightMyRequestResponse, } from 'light-my-request'; import { pathToRegexp } from 'path-to-regexp'; +import { + type VersionValue, + loadPackage, + isString, + isUndefined, +} from '@nestjs/common/internal'; +import { AbstractHttpAdapter } from '@nestjs/core'; +import { LegacyRouteConverter } from '@nestjs/core/internal'; +const { kRouteContext } = fastifySymbols; // Fastify uses `fast-querystring` internally to quickly parse URL query strings. import { parse as querystringParse } from 'fast-querystring'; -import { safeDecodeURI } from 'find-my-way/lib/url-sanitizer'; +import urlSanitizer from 'find-my-way/lib/url-sanitizer.js'; import { FASTIFY_ROUTE_CONFIG_METADATA, FASTIFY_ROUTE_CONSTRAINTS_METADATA, FASTIFY_ROUTE_SCHEMA_METADATA, -} from '../constants'; -import { NestFastifyBodyParserOptions } from '../interfaces'; +} from '../constants.js'; import { FastifyStaticOptions, FastifyViewOptions, -} from '../interfaces/external'; -import middie from './middie/fastify-middie'; +} from '../interfaces/external/index.js'; +import { NestFastifyBodyParserOptions } from '../interfaces/index.js'; +import middie from './middie/fastify-middie.js'; +const { safeDecodeURI } = urlSanitizer; type FastifyAdapterBaseOptions< Server extends RawServerBase = RawServerDefault, @@ -141,7 +149,7 @@ export class FastifyAdapter< FastifyInstance, > extends AbstractHttpAdapter { protected readonly logger = new Logger(FastifyAdapter.name); - protected readonly instance: TInstance; + declare protected readonly instance: TInstance; protected _pathPrefix?: string; private _isParserRegistered: boolean; @@ -555,16 +563,18 @@ export class FastifyAdapter< this.httpServer = this.instance.server; } - public useStaticAssets(options: FastifyStaticOptions) { + public async useStaticAssets(options: FastifyStaticOptions) { return this.register( - loadPackage('@fastify/static', 'FastifyAdapter.useStaticAssets()', () => - require('@fastify/static'), + await loadPackage( + '@fastify/static', + 'FastifyAdapter.useStaticAssets()', + () => import('@fastify/static'), ), options, ); } - public setViewEngine(options: FastifyViewOptions | string) { + public async setViewEngine(options: FastifyViewOptions | string) { if (isString(options)) { new Logger('FastifyAdapter').error( "setViewEngine() doesn't support a string argument.", @@ -572,8 +582,10 @@ export class FastifyAdapter< process.exit(1); } return this.register( - loadPackage('@fastify/view', 'FastifyAdapter.setViewEngine()', () => - require('@fastify/view'), + await loadPackage( + '@fastify/view', + 'FastifyAdapter.setViewEngine()', + () => import('@fastify/view'), ), options, ); @@ -739,6 +751,25 @@ export class FastifyAdapter< return (this.instance.use as any)(...args); } + public mapException(error: unknown): unknown { + if (this.isHttpFastifyError(error)) { + return new HttpException(error.message, error.statusCode); + } + + return error; + } + + private isHttpFastifyError( + error: any, + ): error is Error & { statusCode: number } { + // condition based on this code - https://github.com/fastify/fastify-error/blob/d669b150a82968322f9f7be992b2f6b463272de3/index.js#L22 + return ( + error.statusCode !== undefined && + error instanceof Error && + error.name === 'FastifyError' + ); + } + protected registerWithPrefix( factory: | FastifyPluginCallback @@ -834,7 +865,7 @@ export class FastifyAdapter< handler: handlerRef, }; - if (this.instance.supportedMethods.indexOf(routerMethodKey) === -1) { + if (!this.instance.supportedMethods.includes(routerMethodKey)) { this.instance.addHttpMethod(routerMethodKey, { hasBody: true }); } diff --git a/packages/platform-fastify/adapters/index.ts b/packages/platform-fastify/adapters/index.ts index 109d506ff30..b49c8455ec8 100644 --- a/packages/platform-fastify/adapters/index.ts +++ b/packages/platform-fastify/adapters/index.ts @@ -1 +1 @@ -export * from './fastify-adapter'; +export * from './fastify-adapter.js'; diff --git a/packages/platform-fastify/adapters/middie/fastify-middie.ts b/packages/platform-fastify/adapters/middie/fastify-middie.ts index ca98a27ea7c..f41db77a141 100644 --- a/packages/platform-fastify/adapters/middie/fastify-middie.ts +++ b/packages/platform-fastify/adapters/middie/fastify-middie.ts @@ -8,10 +8,11 @@ import { HookHandlerDoneFunction, } from 'fastify'; import fp from 'fastify-plugin'; -import { safeDecodeURI } from 'find-my-way/lib/url-sanitizer'; +import urlSanitizer from 'find-my-way/lib/url-sanitizer.js'; import * as http from 'node:http'; import { Path, pathToRegexp } from 'path-to-regexp'; -import reusify = require('reusify'); +import reusify from 'reusify'; +const { safeDecodeURI } = urlSanitizer; export type MiddlewareFn< Req extends { url: string; originalUrl?: string }, diff --git a/packages/platform-fastify/decorators/index.ts b/packages/platform-fastify/decorators/index.ts index fce3ca1f746..9a1be313b9e 100644 --- a/packages/platform-fastify/decorators/index.ts +++ b/packages/platform-fastify/decorators/index.ts @@ -1,3 +1,3 @@ -export * from './route-config.decorator'; -export * from './route-constraints.decorator'; -export * from './route-schema.decorator'; +export * from './route-config.decorator.js'; +export * from './route-constraints.decorator.js'; +export * from './route-schema.decorator.js'; diff --git a/packages/platform-fastify/decorators/route-config.decorator.ts b/packages/platform-fastify/decorators/route-config.decorator.ts index b0e6c874fd2..415ae6fac91 100644 --- a/packages/platform-fastify/decorators/route-config.decorator.ts +++ b/packages/platform-fastify/decorators/route-config.decorator.ts @@ -1,5 +1,5 @@ import { SetMetadata } from '@nestjs/common'; -import { FASTIFY_ROUTE_CONFIG_METADATA } from '../constants'; +import { FASTIFY_ROUTE_CONFIG_METADATA } from '../constants.js'; /** * @publicApi diff --git a/packages/platform-fastify/decorators/route-constraints.decorator.ts b/packages/platform-fastify/decorators/route-constraints.decorator.ts index 0b7e4112c5a..707a4b446b2 100644 --- a/packages/platform-fastify/decorators/route-constraints.decorator.ts +++ b/packages/platform-fastify/decorators/route-constraints.decorator.ts @@ -1,5 +1,5 @@ import { SetMetadata } from '@nestjs/common'; -import { FASTIFY_ROUTE_CONSTRAINTS_METADATA } from '../constants'; +import { FASTIFY_ROUTE_CONSTRAINTS_METADATA } from '../constants.js'; import { RouteShorthandOptions } from 'fastify'; /** diff --git a/packages/platform-fastify/decorators/route-schema.decorator.ts b/packages/platform-fastify/decorators/route-schema.decorator.ts index de22c46caf4..e66158452d4 100644 --- a/packages/platform-fastify/decorators/route-schema.decorator.ts +++ b/packages/platform-fastify/decorators/route-schema.decorator.ts @@ -1,5 +1,5 @@ import { SetMetadata } from '@nestjs/common'; -import { FASTIFY_ROUTE_SCHEMA_METADATA } from '../constants'; +import { FASTIFY_ROUTE_SCHEMA_METADATA } from '../constants.js'; import { FastifySchema } from 'fastify'; /** diff --git a/packages/platform-fastify/index.ts b/packages/platform-fastify/index.ts index 7b0f16f3035..86b3d139977 100644 --- a/packages/platform-fastify/index.ts +++ b/packages/platform-fastify/index.ts @@ -5,6 +5,6 @@ * MIT Licensed */ -export * from './adapters'; -export * from './interfaces'; -export * from './decorators'; +export * from './adapters/index.js'; +export * from './interfaces/index.js'; +export * from './decorators/index.js'; diff --git a/packages/platform-fastify/interfaces/external/index.ts b/packages/platform-fastify/interfaces/external/index.ts index a80eb8a365a..1dbf99e30aa 100644 --- a/packages/platform-fastify/interfaces/external/index.ts +++ b/packages/platform-fastify/interfaces/external/index.ts @@ -1,2 +1,2 @@ -export * from './fastify-static-options.interface'; -export * from './fastify-view-options.interface'; +export * from './fastify-static-options.interface.js'; +export * from './fastify-view-options.interface.js'; diff --git a/packages/platform-fastify/interfaces/index.ts b/packages/platform-fastify/interfaces/index.ts index ae18b9a3c50..d88541194e3 100644 --- a/packages/platform-fastify/interfaces/index.ts +++ b/packages/platform-fastify/interfaces/index.ts @@ -1,2 +1,2 @@ -export * from './nest-fastify-application.interface'; -export * from './nest-fastify-body-parser-options.interface'; +export * from './nest-fastify-application.interface.js'; +export * from './nest-fastify-body-parser-options.interface.js'; diff --git a/packages/platform-fastify/interfaces/nest-fastify-application.interface.ts b/packages/platform-fastify/interfaces/nest-fastify-application.interface.ts index d31f00c3485..79168fd7af1 100644 --- a/packages/platform-fastify/interfaces/nest-fastify-application.interface.ts +++ b/packages/platform-fastify/interfaces/nest-fastify-application.interface.ts @@ -1,5 +1,5 @@ import { FastifyCorsOptions } from '@fastify/cors'; -import { HttpServer, INestApplication } from '@nestjs/common'; +import type { HttpServer, INestApplication } from '@nestjs/common'; import { FastifyBodyParser, FastifyInstance, @@ -18,8 +18,8 @@ import { Chain as LightMyRequestChain, Response as LightMyRequestResponse, } from 'light-my-request'; -import { FastifyStaticOptions, FastifyViewOptions } from './external'; -import { NestFastifyBodyParserOptions } from './nest-fastify-body-parser-options.interface'; +import { FastifyStaticOptions, FastifyViewOptions } from './external/index.js'; +import { NestFastifyBodyParserOptions } from './nest-fastify-body-parser-options.interface.js'; /** * @publicApi diff --git a/packages/platform-fastify/package.json b/packages/platform-fastify/package.json index ac7a71c148d..bd5f82e0747 100644 --- a/packages/platform-fastify/package.json +++ b/packages/platform-fastify/package.json @@ -4,6 +4,13 @@ "description": "Nest - modern, fast, powerful node.js web framework (@platform-fastify)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "homepage": "https://nestjs.com", "funding": { "type": "opencollective", diff --git a/packages/platform-fastify/test/adapters/fastify-adapter.spec.ts b/packages/platform-fastify/test/adapters/fastify-adapter.spec.ts new file mode 100644 index 00000000000..dc5699ebc88 --- /dev/null +++ b/packages/platform-fastify/test/adapters/fastify-adapter.spec.ts @@ -0,0 +1,49 @@ +import { FastifyAdapter } from '../../adapters/fastify-adapter'; +import { createError } from '@fastify/error'; +import { HttpException } from '@nestjs/common'; + +describe('FastifyAdapter', () => { + let fastifyAdapter: FastifyAdapter; + + beforeEach(() => { + fastifyAdapter = new FastifyAdapter(); + }); + + afterEach(() => vi.restoreAllMocks()); + + describe('mapException', () => { + it('should map FastifyError with status code to HttpException', () => { + const FastifyErrorCls = createError( + 'FST_ERR_CTP_INVALID_MEDIA_TYPE', + 'Unsupported Media Type: %s', + 415, + ); + const error = new FastifyErrorCls(); + + const result = fastifyAdapter.mapException(error) as HttpException; + + expect(result).to.be.instanceOf(HttpException); + expect(result.message).to.equal(error.message); + expect(result.getStatus()).to.equal(415); + }); + + it('should return FastifyError without user status code to Internal Server Error HttpException', () => { + const FastifyErrorCls = createError( + 'FST_WITHOUT_STATUS_CODE', + 'Error without status code', + ); + const error = new FastifyErrorCls(); + + const result = fastifyAdapter.mapException(error) as HttpException; + expect(result).to.be.instanceOf(HttpException); + expect(result.message).to.equal(error.message); + expect(result.getStatus()).to.equal(500); + }); + + it('should return error if it is not FastifyError', () => { + const error = new Error('Test error'); + const result = fastifyAdapter.mapException(error); + expect(result).to.equal(error); + }); + }); +}); diff --git a/packages/platform-fastify/test/decorators/router-config.decorator.spec.ts b/packages/platform-fastify/test/decorators/router-config.decorator.spec.ts index 3837d04636e..ff73c6c9348 100644 --- a/packages/platform-fastify/test/decorators/router-config.decorator.spec.ts +++ b/packages/platform-fastify/test/decorators/router-config.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { FASTIFY_ROUTE_CONFIG_METADATA } from '../../constants'; -import { RouteConfig } from '../../decorators/route-config.decorator'; +import { FASTIFY_ROUTE_CONFIG_METADATA } from '../../constants.js'; +import { RouteConfig } from '../../decorators/route-config.decorator.js'; describe('@RouteConfig', () => { const routeConfig = { testKey: 'testValue' }; @@ -12,6 +11,6 @@ describe('@RouteConfig', () => { it('should enhance method with expected fastify route config', () => { const path = Reflect.getMetadata(FASTIFY_ROUTE_CONFIG_METADATA, Test.test); - expect(path).to.be.eql(routeConfig); + expect(path).toEqual(routeConfig); }); }); diff --git a/packages/platform-fastify/test/decorators/router-constraints.decorator.spec.ts b/packages/platform-fastify/test/decorators/router-constraints.decorator.spec.ts index ecd43f7f6a5..789e274fb0e 100644 --- a/packages/platform-fastify/test/decorators/router-constraints.decorator.spec.ts +++ b/packages/platform-fastify/test/decorators/router-constraints.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { FASTIFY_ROUTE_CONSTRAINTS_METADATA } from '../../constants'; -import { RouteConstraints } from '../../decorators/route-constraints.decorator'; +import { FASTIFY_ROUTE_CONSTRAINTS_METADATA } from '../../constants.js'; +import { RouteConstraints } from '../../decorators/route-constraints.decorator.js'; describe('@RouteConstraints', () => { describe('has version constraints', () => { @@ -16,7 +15,7 @@ describe('@RouteConstraints', () => { FASTIFY_ROUTE_CONSTRAINTS_METADATA, TestVersionConstraints.test, ); - expect(path).to.be.eql(routeConstraints); + expect(path).toEqual(routeConstraints); }); }); @@ -33,7 +32,7 @@ describe('@RouteConstraints', () => { FASTIFY_ROUTE_CONSTRAINTS_METADATA, TestConstraints.test, ); - expect(path).to.be.eql(customRouteConstraints); + expect(path).toEqual(customRouteConstraints); }); }); }); diff --git a/packages/platform-fastify/test/decorators/router-schema.decorator.spec.ts b/packages/platform-fastify/test/decorators/router-schema.decorator.spec.ts index 22ff3471cb6..3e5d7047865 100644 --- a/packages/platform-fastify/test/decorators/router-schema.decorator.spec.ts +++ b/packages/platform-fastify/test/decorators/router-schema.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { FASTIFY_ROUTE_SCHEMA_METADATA } from '../../constants'; -import { RouteSchema } from '../../decorators/route-schema.decorator'; +import { FASTIFY_ROUTE_SCHEMA_METADATA } from '../../constants.js'; +import { RouteSchema } from '../../decorators/route-schema.decorator.js'; describe('@RouteSchema', () => { const routeSchema = { body: 'testValue' }; @@ -12,6 +11,6 @@ describe('@RouteSchema', () => { it('should enhance method with expected fastify route schema', () => { const path = Reflect.getMetadata(FASTIFY_ROUTE_SCHEMA_METADATA, Test.test); - expect(path).to.be.eql(routeSchema); + expect(path).toEqual(routeSchema); }); }); diff --git a/packages/platform-fastify/tsconfig.build.json b/packages/platform-fastify/tsconfig.build.json index 732ca00cb71..fb0f617fdbe 100644 --- a/packages/platform-fastify/tsconfig.build.json +++ b/packages/platform-fastify/tsconfig.build.json @@ -2,13 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/core": ["../core"], - "@nestjs/core/*": ["../core/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/platform-socket.io/adapters/index.ts b/packages/platform-socket.io/adapters/index.ts index 97b4b62692b..df174bfcb61 100644 --- a/packages/platform-socket.io/adapters/index.ts +++ b/packages/platform-socket.io/adapters/index.ts @@ -1 +1 @@ -export * from './io-adapter'; +export * from './io-adapter.js'; diff --git a/packages/platform-socket.io/adapters/io-adapter.ts b/packages/platform-socket.io/adapters/io-adapter.ts index 109041daf40..5044829843c 100644 --- a/packages/platform-socket.io/adapters/io-adapter.ts +++ b/packages/platform-socket.io/adapters/io-adapter.ts @@ -1,12 +1,12 @@ -import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; import { AbstractWsAdapter, - MessageMappingProperties, + type MessageMappingProperties, } from '@nestjs/websockets'; -import { DISCONNECT_EVENT } from '@nestjs/websockets/constants'; import { fromEvent, Observable } from 'rxjs'; import { filter, first, map, mergeMap, share, takeUntil } from 'rxjs/operators'; import { Server, ServerOptions, Socket } from 'socket.io'; +import { isFunction, isNil } from '@nestjs/common/internal'; +import { DISCONNECT_EVENT } from '@nestjs/websockets/internal'; /** * @publicApi @@ -85,6 +85,10 @@ export class IoAdapter extends AbstractWsAdapter { return { data: payload }; } + public bindClientDisconnect(client: Socket, callback: Function) { + client.on(DISCONNECT_EVENT, (reason: string) => callback(reason)); + } + public async close(server: Server): Promise { if (this.forceCloseConnections && server.httpServer === this.httpServer) { return; diff --git a/packages/platform-socket.io/index.ts b/packages/platform-socket.io/index.ts index 818f1a0146f..d2ecbacb220 100644 --- a/packages/platform-socket.io/index.ts +++ b/packages/platform-socket.io/index.ts @@ -5,4 +5,4 @@ * MIT Licensed */ -export * from './adapters'; +export * from './adapters/index.js'; diff --git a/packages/platform-socket.io/package.json b/packages/platform-socket.io/package.json index fdf0d71e58d..44e44a42dd7 100644 --- a/packages/platform-socket.io/package.json +++ b/packages/platform-socket.io/package.json @@ -4,6 +4,13 @@ "description": "Nest - modern, fast, powerful node.js web framework (@platform-socket.io)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "homepage": "https://nestjs.com", "funding": { "type": "opencollective", diff --git a/packages/platform-socket.io/tsconfig.build.json b/packages/platform-socket.io/tsconfig.build.json index b9ccddc2226..448fade51b1 100644 --- a/packages/platform-socket.io/tsconfig.build.json +++ b/packages/platform-socket.io/tsconfig.build.json @@ -2,13 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/websockets": ["../websockets"], - "@nestjs/websockets/*": ["../websockets/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/platform-ws/adapters/index.ts b/packages/platform-ws/adapters/index.ts index 0f0c68ef87b..3f863af5b11 100644 --- a/packages/platform-ws/adapters/index.ts +++ b/packages/platform-ws/adapters/index.ts @@ -1 +1 @@ -export * from './ws-adapter'; +export * from './ws-adapter.js'; diff --git a/packages/platform-ws/adapters/ws-adapter.ts b/packages/platform-ws/adapters/ws-adapter.ts index 6292468ad89..84c3976249d 100644 --- a/packages/platform-ws/adapters/ws-adapter.ts +++ b/packages/platform-ws/adapters/ws-adapter.ts @@ -1,16 +1,16 @@ -import { INestApplicationContext, Logger } from '@nestjs/common'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isNil, normalizePath } from '@nestjs/common/utils/shared.utils'; +import { type INestApplicationContext, Logger } from '@nestjs/common'; import { AbstractWsAdapter } from '@nestjs/websockets'; +import * as http from 'http'; +import { createRequire } from 'module'; +import { EMPTY, fromEvent, Observable } from 'rxjs'; +import { filter, first, mergeMap, share, takeUntil } from 'rxjs/operators'; +import { loadPackageSync, isNil, normalizePath } from '@nestjs/common/internal'; import { CLOSE_EVENT, CONNECTION_EVENT, ERROR_EVENT, -} from '@nestjs/websockets/constants'; -import { MessageMappingProperties } from '@nestjs/websockets/gateway-metadata-explorer'; -import * as http from 'http'; -import { EMPTY, fromEvent, Observable } from 'rxjs'; -import { filter, first, mergeMap, share, takeUntil } from 'rxjs/operators'; +} from '@nestjs/websockets/internal'; +import type { MessageMappingProperties } from '@nestjs/websockets'; let wsPackage: any = {}; @@ -55,7 +55,14 @@ export class WsAdapter extends AbstractWsAdapter { options?: WsAdapterOptions, ) { super(appOrHttpServer); - wsPackage = loadPackage('ws', 'WsAdapter', () => require('ws')); + wsPackage = loadPackageSync('ws', 'WsAdapter', () => + createRequire(import.meta.url)('ws'), + ); + // Normalize CJS/ESM: In CJS, require('ws') returns WebSocket with .Server. + // We need .Server for creating WebSocketServer instances. + if (!wsPackage.Server) { + wsPackage = { ...wsPackage, Server: wsPackage.WebSocketServer }; + } if (options?.messageParser) { this.messageParser = options.messageParser; @@ -185,8 +192,10 @@ export class WsAdapter extends AbstractWsAdapter { const closeEventSignal = new Promise((resolve, reject) => server.close((err: Error) => (err ? reject(err) : resolve(undefined))), ); - for (const ws of server.clients) { - ws.terminate(); + if (server.clients) { + for (const ws of server.clients) { + ws.terminate(); + } } await closeEventSignal; } @@ -234,7 +243,7 @@ export class WsAdapter extends AbstractWsAdapter { socket.destroy(); } } catch (err) { - socket.end('HTTP/1.1 400\r\n' + err.message); + socket.end(`HTTP/1.1 400\r\n${err.message}`); } }); return httpServer; diff --git a/packages/platform-ws/index.ts b/packages/platform-ws/index.ts index 53c99aa8d8e..5660ae37083 100644 --- a/packages/platform-ws/index.ts +++ b/packages/platform-ws/index.ts @@ -5,4 +5,4 @@ * MIT Licensed */ -export * from './adapters'; +export * from './adapters/index.js'; diff --git a/packages/platform-ws/package.json b/packages/platform-ws/package.json index 902f4a4889e..21b1c6c7e06 100644 --- a/packages/platform-ws/package.json +++ b/packages/platform-ws/package.json @@ -4,6 +4,13 @@ "description": "Nest - modern, fast, powerful node.js web framework (@platform-ws)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "homepage": "https://nestjs.com", "funding": { "type": "opencollective", diff --git a/packages/platform-ws/tsconfig.build.json b/packages/platform-ws/tsconfig.build.json index b9ccddc2226..448fade51b1 100644 --- a/packages/platform-ws/tsconfig.build.json +++ b/packages/platform-ws/tsconfig.build.json @@ -2,13 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/websockets": ["../websockets"], - "@nestjs/websockets/*": ["../websockets/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/testing/index.ts b/packages/testing/index.ts index 03a845c421c..03af9654ac4 100644 --- a/packages/testing/index.ts +++ b/packages/testing/index.ts @@ -5,7 +5,7 @@ * MIT Licensed */ -export * from './interfaces'; -export * from './test'; -export * from './testing-module'; -export * from './testing-module.builder'; +export * from './interfaces/index.js'; +export * from './test.js'; +export * from './testing-module.js'; +export * from './testing-module.builder.js'; diff --git a/packages/testing/interfaces/index.ts b/packages/testing/interfaces/index.ts index 4f5aa2fa6cc..27ae6544f9d 100644 --- a/packages/testing/interfaces/index.ts +++ b/packages/testing/interfaces/index.ts @@ -1,3 +1,3 @@ -export * from './mock-factory'; -export * from './override-by-factory-options.interface'; -export * from './override-by.interface'; +export * from './mock-factory.js'; +export * from './override-by-factory-options.interface.js'; +export * from './override-by.interface.js'; diff --git a/packages/testing/interfaces/mock-factory.ts b/packages/testing/interfaces/mock-factory.ts index 0b67435ea0d..471c5217357 100644 --- a/packages/testing/interfaces/mock-factory.ts +++ b/packages/testing/interfaces/mock-factory.ts @@ -1,3 +1,3 @@ -import { InjectionToken } from '@nestjs/common'; +import type { InjectionToken } from '@nestjs/common'; export type MockFactory = (token?: InjectionToken) => any; diff --git a/packages/testing/interfaces/override-by.interface.ts b/packages/testing/interfaces/override-by.interface.ts index fafc82e468a..505db976a26 100644 --- a/packages/testing/interfaces/override-by.interface.ts +++ b/packages/testing/interfaces/override-by.interface.ts @@ -1,5 +1,5 @@ -import { TestingModuleBuilder } from '../testing-module.builder'; -import { OverrideByFactoryOptions } from './override-by-factory-options.interface'; +import { TestingModuleBuilder } from '../testing-module.builder.js'; +import { OverrideByFactoryOptions } from './override-by-factory-options.interface.js'; /** * @publicApi diff --git a/packages/testing/interfaces/override-module.interface.ts b/packages/testing/interfaces/override-module.interface.ts index 1bebdd4acba..9f221c2a072 100644 --- a/packages/testing/interfaces/override-module.interface.ts +++ b/packages/testing/interfaces/override-module.interface.ts @@ -1,5 +1,5 @@ -import { ModuleDefinition } from '@nestjs/core/interfaces/module-definition.interface'; -import { TestingModuleBuilder } from '../testing-module.builder'; +import { TestingModuleBuilder } from '../testing-module.builder.js'; +import type { ModuleDefinition } from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/testing/package.json b/packages/testing/package.json index c3dda72cd50..e49a8daa3c3 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -4,6 +4,13 @@ "description": "Nest - modern, fast, powerful node.js web framework (@testing)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "homepage": "https://nestjs.com", "funding": { "type": "opencollective", diff --git a/packages/testing/test.ts b/packages/testing/test.ts index 1ddf658d6c6..8b10d36ba99 100644 --- a/packages/testing/test.ts +++ b/packages/testing/test.ts @@ -1,9 +1,9 @@ -import { ModuleMetadata } from '@nestjs/common/interfaces/modules/module-metadata.interface'; -import { MetadataScanner } from '@nestjs/core/metadata-scanner'; import { TestingModuleBuilder, TestingModuleOptions, -} from './testing-module.builder'; +} from './testing-module.builder.js'; +import type { ModuleMetadata } from '@nestjs/common'; +import { MetadataScanner } from '@nestjs/core'; export class Test { private static readonly metadataScanner = new MetadataScanner(); diff --git a/packages/testing/test/testing-injector.spec.ts b/packages/testing/test/testing-injector.spec.ts new file mode 100644 index 00000000000..966a848e96a --- /dev/null +++ b/packages/testing/test/testing-injector.spec.ts @@ -0,0 +1,171 @@ +import { TestingInjector } from '../testing-injector.js'; + +describe('TestingInjector', () => { + let injector: TestingInjector; + + beforeEach(() => { + injector = new TestingInjector({ preview: false }); + }); + + describe('setMocker', () => { + it('should store the mocker factory', () => { + const mocker = vi.fn(); + injector.setMocker(mocker); + + expect(injector['mocker']).toBe(mocker); + }); + }); + + describe('setContainer', () => { + it('should store the container reference', () => { + const container = { getModules: vi.fn() } as any; + injector.setContainer(container); + + expect(injector['container']).toBe(container); + }); + }); + + describe('resolveComponentWrapper', () => { + it('should delegate to super and return existing wrapper on success', async () => { + const expectedWrapper = { name: 'TestWrapper' }; + vi.spyOn( + Object.getPrototypeOf(TestingInjector.prototype), + 'resolveComponentWrapper', + ).mockResolvedValue(expectedWrapper); + + const result = await injector.resolveComponentWrapper( + {} as any, + 'TestToken', + {} as any, + {} as any, + ); + + expect(result).toBe(expectedWrapper); + }); + + it('should throw when super fails and no mocker is set', async () => { + vi.spyOn( + Object.getPrototypeOf(TestingInjector.prototype), + 'resolveComponentWrapper', + ).mockRejectedValue(new Error('Not found')); + + await expect( + injector.resolveComponentWrapper( + {} as any, + 'TestToken', + {} as any, + {} as any, + ), + ).rejects.toThrow('Not found'); + }); + + it('should use mocker when super fails and mocker is set', async () => { + vi.spyOn( + Object.getPrototypeOf(TestingInjector.prototype), + 'resolveComponentWrapper', + ).mockRejectedValue(new Error('Not found')); + + const mockInstance = { mocked: true }; + const mocker = vi.fn().mockReturnValue(mockInstance); + injector.setMocker(mocker); + + const mockContainer = { + getInternalCoreModuleRef: vi.fn().mockReturnValue({ + addCustomProvider: vi.fn(), + addExportedProviderOrModule: vi.fn(), + providers: new Map(), + }), + } as any; + injector.setContainer(mockContainer); + + const result = await injector.resolveComponentWrapper( + {} as any, + 'TestToken', + {} as any, + { metatype: class {} as any, scope: 0 } as any, + ); + + expect(mocker).toHaveBeenCalledWith('TestToken'); + expect(result.instance).toBe(mockInstance); + }); + + it('should throw when mocker returns falsy value', async () => { + vi.spyOn( + Object.getPrototypeOf(TestingInjector.prototype), + 'resolveComponentWrapper', + ).mockRejectedValue(new Error('Not found')); + + const mocker = vi.fn().mockReturnValue(undefined); + injector.setMocker(mocker); + + await expect( + injector.resolveComponentWrapper( + {} as any, + 'TestToken', + {} as any, + {} as any, + ), + ).rejects.toThrow('Not found'); + }); + }); + + describe('resolveComponentHost', () => { + it('should delegate to super and return existing wrapper on success', async () => { + const expectedWrapper = { name: 'TestWrapper' }; + vi.spyOn( + Object.getPrototypeOf(TestingInjector.prototype), + 'resolveComponentHost', + ).mockResolvedValue(expectedWrapper); + + const result = await injector.resolveComponentHost( + {} as any, + { name: 'TestName' } as any, + ); + + expect(result).toBe(expectedWrapper); + }); + + it('should throw when super fails and no mocker is set', async () => { + vi.spyOn( + Object.getPrototypeOf(TestingInjector.prototype), + 'resolveComponentHost', + ).mockRejectedValue(new Error('Not found')); + + await expect( + injector.resolveComponentHost({} as any, { name: 'TestName' } as any), + ).rejects.toThrow('Not found'); + }); + + it('should use mocker when super fails and mocker is set', async () => { + vi.spyOn( + Object.getPrototypeOf(TestingInjector.prototype), + 'resolveComponentHost', + ).mockRejectedValue(new Error('Not found')); + + const mockInstance = { mocked: true }; + const mocker = vi.fn().mockReturnValue(mockInstance); + injector.setMocker(mocker); + + const mockContainer = { + getInternalCoreModuleRef: vi.fn().mockReturnValue({ + addCustomProvider: vi.fn(), + addExportedProviderOrModule: vi.fn(), + providers: new Map(), + }), + } as any; + injector.setContainer(mockContainer); + + const result = await injector.resolveComponentHost( + {} as any, + { name: 'TestName', metatype: class {} as any, scope: 0 } as any, + ); + + expect(mocker).toHaveBeenCalledWith('TestName'); + expect(result.instance).toBe(mockInstance); + }); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); +}); diff --git a/packages/testing/test/testing-instance-loader.spec.ts b/packages/testing/test/testing-instance-loader.spec.ts new file mode 100644 index 00000000000..9a33c76bd13 --- /dev/null +++ b/packages/testing/test/testing-instance-loader.spec.ts @@ -0,0 +1,76 @@ +import { TestingInstanceLoader } from '../testing-instance-loader.js'; + +describe('TestingInstanceLoader', () => { + let loader: TestingInstanceLoader; + let mockInjector: any; + let mockContainer: any; + + beforeEach(() => { + mockInjector = { + setContainer: vi.fn(), + setMocker: vi.fn(), + }; + mockContainer = { + getModules: vi.fn().mockReturnValue(new Map()), + }; + + // TestingInstanceLoader extends InstanceLoader + // We create it with the required constructor args + loader = new TestingInstanceLoader( + mockContainer, + mockInjector, + {} as any, // graphInspector + ); + + // Mock super.createInstancesOfDependencies to avoid running the real loader + vi.spyOn( + Object.getPrototypeOf(TestingInstanceLoader.prototype), + 'createInstancesOfDependencies', + ).mockResolvedValue(undefined); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('createInstancesOfDependencies', () => { + it('should set the container on the injector', async () => { + await loader.createInstancesOfDependencies(); + + expect(mockInjector.setContainer).toHaveBeenCalledWith(mockContainer); + }); + + it('should not set mocker when none is provided', async () => { + await loader.createInstancesOfDependencies(); + + expect(mockInjector.setMocker).not.toHaveBeenCalled(); + }); + + it('should set mocker when provided', async () => { + const mocker = vi.fn(); + await loader.createInstancesOfDependencies(undefined, mocker); + + expect(mockInjector.setMocker).toHaveBeenCalledWith(mocker); + }); + + it('should call super.createInstancesOfDependencies', async () => { + const superSpy = vi.spyOn( + Object.getPrototypeOf(TestingInstanceLoader.prototype), + 'createInstancesOfDependencies', + ); + + await loader.createInstancesOfDependencies(); + + expect(superSpy).toHaveBeenCalled(); + }); + + it('should use default modules from container when none provided', async () => { + const modules = new Map([['key', { id: 'mod1' }]]); + mockContainer.getModules.mockReturnValue(modules); + + await loader.createInstancesOfDependencies(); + + expect(mockContainer.getModules).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/testing/test/testing-logger.service.spec.ts b/packages/testing/test/testing-logger.service.spec.ts new file mode 100644 index 00000000000..5ca1d2b4470 --- /dev/null +++ b/packages/testing/test/testing-logger.service.spec.ts @@ -0,0 +1,56 @@ +import { TestingLogger } from '../services/testing-logger.service.js'; + +describe('TestingLogger', () => { + let logger: TestingLogger; + let superErrorSpy: ReturnType; + + beforeEach(() => { + logger = new TestingLogger(); + superErrorSpy = vi + .spyOn(Object.getPrototypeOf(TestingLogger.prototype), 'error') + .mockImplementation(() => {}); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('log', () => { + it('should be a no-op', () => { + expect(() => logger.log('message')).not.toThrow(); + }); + }); + + describe('warn', () => { + it('should be a no-op', () => { + expect(() => logger.warn('message')).not.toThrow(); + }); + }); + + describe('debug', () => { + it('should be a no-op', () => { + expect(() => logger.debug('message')).not.toThrow(); + }); + }); + + describe('verbose', () => { + it('should be a no-op', () => { + expect(() => logger.verbose('message')).not.toThrow(); + }); + }); + + describe('error', () => { + it('should delegate to super.error', () => { + logger.error('something went wrong'); + expect(superErrorSpy).toHaveBeenCalledWith('something went wrong'); + }); + + it('should pass optional params to super.error', () => { + logger.error('error message', 'stack trace'); + expect(superErrorSpy).toHaveBeenCalledWith( + 'error message', + 'stack trace', + ); + }); + }); +}); diff --git a/packages/testing/test/tsconfig.json b/packages/testing/test/tsconfig.json new file mode 100644 index 00000000000..50d59661da9 --- /dev/null +++ b/packages/testing/test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tsconfig.spec.json" +} diff --git a/packages/testing/testing-injector.ts b/packages/testing/testing-injector.ts index c5a783196b5..1580e74fd66 100644 --- a/packages/testing/testing-injector.ts +++ b/packages/testing/testing-injector.ts @@ -1,12 +1,12 @@ -import { NestContainer } from '@nestjs/core'; -import { STATIC_CONTEXT } from '@nestjs/core/injector/constants'; +import type { NestContainer } from '@nestjs/core'; +import { MockFactory } from './interfaces/index.js'; import { + STATIC_CONTEXT, Injector, - InjectorDependencyContext, -} from '@nestjs/core/injector/injector'; -import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; -import { Module } from '@nestjs/core/injector/module'; -import { MockFactory } from './interfaces'; + type InjectorDependencyContext, + InstanceWrapper, + type Module, +} from '@nestjs/core/internal'; /** * @publicApi diff --git a/packages/testing/testing-instance-loader.ts b/packages/testing/testing-instance-loader.ts index 6c4aead4828..e84ed19f3ea 100644 --- a/packages/testing/testing-instance-loader.ts +++ b/packages/testing/testing-instance-loader.ts @@ -1,7 +1,6 @@ -import { InstanceLoader } from '@nestjs/core/injector/instance-loader'; -import { Module } from '@nestjs/core/injector/module'; -import { MockFactory } from './interfaces'; -import { TestingInjector } from './testing-injector'; +import { MockFactory } from './interfaces/index.js'; +import { TestingInjector } from './testing-injector.js'; +import { InstanceLoader, type Module } from '@nestjs/core/internal'; export class TestingInstanceLoader extends InstanceLoader { public async createInstancesOfDependencies( diff --git a/packages/testing/testing-module.builder.ts b/packages/testing/testing-module.builder.ts index ca5d654943e..aa90d37377c 100644 --- a/packages/testing/testing-module.builder.ts +++ b/packages/testing/testing-module.builder.ts @@ -1,27 +1,34 @@ -import { Logger, LoggerService, Module, ModuleMetadata } from '@nestjs/common'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; -import { NoopGraphInspector } from '@nestjs/core/inspector/noop-graph-inspector'; import { - UuidFactory, - UuidFactoryMode, -} from '@nestjs/core/inspector/uuid-factory'; -import { ModuleDefinition } from '@nestjs/core/interfaces/module-definition.interface'; -import { ModuleOverride } from '@nestjs/core/interfaces/module-override.interface'; -import { MetadataScanner } from '@nestjs/core/metadata-scanner'; -import { DependenciesScanner } from '@nestjs/core/scanner'; + Logger, + type LoggerService, + Module, + type ModuleMetadata, +} from '@nestjs/common'; import { MockFactory, OverrideBy, OverrideByFactoryOptions, -} from './interfaces'; -import { OverrideModule } from './interfaces/override-module.interface'; -import { TestingLogger } from './services/testing-logger.service'; -import { TestingInjector } from './testing-injector'; -import { TestingInstanceLoader } from './testing-instance-loader'; -import { TestingModule } from './testing-module'; +} from './interfaces/index.js'; +import { OverrideModule } from './interfaces/override-module.interface.js'; +import { TestingLogger } from './services/testing-logger.service.js'; +import { TestingInjector } from './testing-injector.js'; +import { TestingInstanceLoader } from './testing-instance-loader.js'; +import { TestingModule } from './testing-module.js'; +import type { NestApplicationContextOptions } from '@nestjs/common/internal'; +import { + ApplicationConfig, + NestContainer, + GraphInspector, + type MetadataScanner, +} from '@nestjs/core'; +import { + NoopGraphInspector, + UuidFactory, + UuidFactoryMode, + type ModuleDefinition, + type ModuleOverride, + DependenciesScanner, +} from '@nestjs/core/internal'; /** * @publicApi @@ -123,12 +130,14 @@ export class TestingModuleBuilder { scanner.applyApplicationProviders(); const root = this.getRootModule(); - return new TestingModule( + const testingModule = new TestingModule( this.container, graphInspector, root, this.applicationConfig, ); + await testingModule['preloadLazyPackages'](); + return testingModule; } private override(typeOrToken: T, isProvider: boolean): OverrideBy { diff --git a/packages/testing/testing-module.ts b/packages/testing/testing-module.ts index 79d2e86c343..d118cb09dd1 100644 --- a/packages/testing/testing-module.ts +++ b/packages/testing/testing-module.ts @@ -1,24 +1,29 @@ import { - HttpServer, - INestApplication, - INestMicroservice, + type HttpServer, + type INestApplication, + type INestMicroservice, Logger, - NestApplicationOptions, - Type, + type NestApplicationOptions, + type Type, } from '@nestjs/common'; -import { NestMicroserviceOptions } from '@nestjs/common/interfaces/microservices/nest-microservice-options.interface'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { isUndefined } from '@nestjs/common/utils/shared.utils'; import { - AbstractHttpAdapter, + type AbstractHttpAdapter, NestApplication, NestApplicationContext, } from '@nestjs/core'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { Module } from '@nestjs/core/injector/module'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; +import { + type NestMicroserviceOptions, + type NestApplicationContextOptions, + loadPackage, + loadPackageCached, + isUndefined, +} from '@nestjs/common/internal'; +import type { + ApplicationConfig, + NestContainer, + GraphInspector, +} from '@nestjs/core'; +import type { Module } from '@nestjs/core/internal'; /** * @publicApi @@ -39,6 +44,25 @@ export class TestingModule extends NestApplicationContext { this.graphInspector = graphInspector; } + /** + * Pre-load optional packages so that createNestApplication, + * createNestMicroservice and createHttpAdapter can stay synchronous. + * Called from TestingModuleBuilder.compile(). + */ + private async preloadLazyPackages(): Promise { + // Best-effort: silently swallow if packages are not installed + await loadPackage( + '@nestjs/platform-express', + 'TestingModule', + () => import('@nestjs/platform-express'), + ).catch(() => {}); + await loadPackage( + '@nestjs/microservices', + 'TestingModule', + () => import('@nestjs/microservices'), + ).catch(() => {}); + } + private isHttpServer( serverOrOptions: | HttpServer @@ -84,11 +108,7 @@ export class TestingModule extends NestApplicationContext { public createNestMicroservice( options: NestMicroserviceOptions & T, ): INestMicroservice { - const { NestMicroservice } = loadPackage( - '@nestjs/microservices', - 'TestingModule', - () => require('@nestjs/microservices'), - ); + const { NestMicroservice } = loadPackageCached('@nestjs/microservices'); this.applyLogger(options); return new NestMicroservice( this.container, @@ -99,11 +119,7 @@ export class TestingModule extends NestApplicationContext { } private createHttpAdapter(httpServer?: T): AbstractHttpAdapter { - const { ExpressAdapter } = loadPackage( - '@nestjs/platform-express', - 'NestFactory', - () => require('@nestjs/platform-express'), - ); + const { ExpressAdapter } = loadPackageCached('@nestjs/platform-express'); return new ExpressAdapter(httpServer); } diff --git a/packages/testing/tsconfig.build.json b/packages/testing/tsconfig.build.json index a5a3a630596..a08f7f7fdb5 100644 --- a/packages/testing/tsconfig.build.json +++ b/packages/testing/tsconfig.build.json @@ -2,17 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/core": ["../core"], - "@nestjs/core/*": ["../core/*"], - "@nestjs/microservices": ["../microservices"], - "@nestjs/microservices/*": ["../microservices/*"], - "@nestjs/platform-express": ["../platform-express"], - "@nestjs/platform-express/*": ["../platform-express/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/tsconfig.build.json b/packages/tsconfig.build.json index e1bdb8e40dd..289cca76979 100644 --- a/packages/tsconfig.build.json +++ b/packages/tsconfig.build.json @@ -1,7 +1,9 @@ { "compilerOptions": { "composite": true, - "module": "commonjs", + "module": "Node16", + "moduleResolution": "Node16", + "esModuleInterop": true, "declaration": true, "noImplicitAny": false, "skipLibCheck": true, @@ -12,7 +14,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "useUnknownInCatchVariables": false, - "target": "ES2021", + "target": "ES2023", "sourceMap": false, "allowJs": false, "strict": true, diff --git a/packages/websockets/adapters/index.ts b/packages/websockets/adapters/index.ts index 0f0c68ef87b..3f863af5b11 100644 --- a/packages/websockets/adapters/index.ts +++ b/packages/websockets/adapters/index.ts @@ -1 +1 @@ -export * from './ws-adapter'; +export * from './ws-adapter.js'; diff --git a/packages/websockets/adapters/ws-adapter.ts b/packages/websockets/adapters/ws-adapter.ts index 62470b688f5..fe1439c2a50 100644 --- a/packages/websockets/adapters/ws-adapter.ts +++ b/packages/websockets/adapters/ws-adapter.ts @@ -1,9 +1,9 @@ -import { INestApplicationContext, WebSocketAdapter } from '@nestjs/common'; -import { WsMessageHandler } from '@nestjs/common/interfaces'; -import { isFunction } from '@nestjs/common/utils/shared.utils'; +import type { INestApplicationContext, WebSocketAdapter } from '@nestjs/common'; import { NestApplication } from '@nestjs/core'; import { Observable } from 'rxjs'; -import { CONNECTION_EVENT, DISCONNECT_EVENT } from '../constants'; +import { CONNECTION_EVENT, DISCONNECT_EVENT } from '../constants.js'; +import type { WsMessageHandler } from '@nestjs/common'; +import { isFunction } from '@nestjs/common/internal'; export interface BaseWsInstance { on: (event: string, callback: Function) => void; diff --git a/packages/websockets/constants.ts b/packages/websockets/constants.ts index a1434629296..e8b7b8ed367 100644 --- a/packages/websockets/constants.ts +++ b/packages/websockets/constants.ts @@ -1,4 +1,4 @@ -import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants'; +import { ROUTE_ARGS_METADATA } from '@nestjs/common/internal'; export const MESSAGE_MAPPING_METADATA = 'websockets:message_mapping'; export const MESSAGE_METADATA = 'message'; diff --git a/packages/websockets/context/exception-filters-context.ts b/packages/websockets/context/exception-filters-context.ts index 7d630ad1cc6..c89de4a46c9 100644 --- a/packages/websockets/context/exception-filters-context.ts +++ b/packages/websockets/context/exception-filters-context.ts @@ -1,8 +1,10 @@ -import { EXCEPTION_FILTERS_METADATA } from '@nestjs/common/constants'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { BaseExceptionFilterContext } from '@nestjs/core/exceptions/base-exception-filter-context'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { WsExceptionsHandler } from '../exceptions/ws-exceptions-handler'; +import { WsExceptionsHandler } from '../exceptions/ws-exceptions-handler.js'; +import { + EXCEPTION_FILTERS_METADATA, + isEmptyArray, +} from '@nestjs/common/internal'; +import { BaseExceptionFilterContext } from '@nestjs/core/internal'; +import type { NestContainer } from '@nestjs/core'; /** * @publicApi @@ -25,7 +27,7 @@ export class ExceptionFiltersContext extends BaseExceptionFilterContext { callback, EXCEPTION_FILTERS_METADATA, ); - if (isEmpty(filters)) { + if (isEmptyArray(filters)) { return exceptionHandler; } exceptionHandler.setCustomFilters(filters.reverse()); diff --git a/packages/websockets/context/ws-context-creator.ts b/packages/websockets/context/ws-context-creator.ts index c699612b6f7..e9c7e3d7189 100644 --- a/packages/websockets/context/ws-context-creator.ts +++ b/packages/websockets/context/ws-context-creator.ts @@ -1,34 +1,34 @@ +import type { + ArgumentMetadata, + ContextType, + PipeTransform, +} from '@nestjs/common'; import { + type Controller, CUSTOM_ROUTE_ARGS_METADATA, + isEmptyArray, PARAMTYPES_METADATA, -} from '@nestjs/common/constants'; -import { - ContextType, - Controller, - PipeTransform, -} from '@nestjs/common/interfaces'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { FORBIDDEN_MESSAGE } from '@nestjs/core/guards/constants'; -import { GuardsConsumer } from '@nestjs/core/guards/guards-consumer'; -import { GuardsContextCreator } from '@nestjs/core/guards/guards-context-creator'; +} from '@nestjs/common/internal'; import { ContextUtils, - ParamProperties, -} from '@nestjs/core/helpers/context-utils'; -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { HandlerMetadataStorage } from '@nestjs/core/helpers/handler-metadata-storage'; -import { ParamsMetadata } from '@nestjs/core/helpers/interfaces'; -import { - InterceptorsConsumer, - InterceptorsContextCreator, -} from '@nestjs/core/interceptors'; -import { PipesConsumer, PipesContextCreator } from '@nestjs/core/pipes'; -import { MESSAGE_METADATA, PARAM_ARGS_METADATA } from '../constants'; -import { WsException } from '../errors/ws-exception'; -import { WsParamsFactory } from '../factories/ws-params-factory'; -import { ExceptionFiltersContext } from './exception-filters-context'; -import { DEFAULT_CALLBACK_METADATA } from './ws-metadata-constants'; -import { WsProxy } from './ws-proxy'; + type ExecutionContextHost, + FORBIDDEN_MESSAGE, + type GuardsConsumer, + type GuardsContextCreator, + HandlerMetadataStorage, + type InterceptorsConsumer, + type InterceptorsContextCreator, + type ParamProperties, + type ParamsMetadata, + type PipesConsumer, + type PipesContextCreator, +} from '@nestjs/core/internal'; +import { MESSAGE_METADATA, PARAM_ARGS_METADATA } from '../constants.js'; +import { WsException } from '../errors/ws-exception.js'; +import { WsParamsFactory } from '../factories/ws-params-factory.js'; +import { ExceptionFiltersContext } from './exception-filters-context.js'; +import { DEFAULT_CALLBACK_METADATA } from './ws-metadata-constants.js'; +import { WsProxy } from './ws-proxy.js'; type WsParamProperties = ParamProperties & { metatype?: any }; export interface WsHandlerMetadata { @@ -215,7 +215,7 @@ export class WsContextCreator { this.pipesContextCreator.setModuleContext(moduleContext); return keys.map(key => { - const { index, data, pipes: pipesCollection } = metadata[key]; + const { index, data, pipes: pipesCollection, schema } = metadata[key]; const pipes = this.pipesContextCreator.createConcreteContext(pipesCollection); const type = this.contextUtils.mapParamType(key); @@ -227,13 +227,20 @@ export class WsContextCreator { data, contextFactory, ); - return { index, extractValue: customExtractValue, type, data, pipes }; + return { + index, + extractValue: customExtractValue, + type, + data, + pipes, + schema, + }; } const numericType = Number(type); const extractValue = (...args: any[]) => paramsFactory.exchangeKeyForValue(numericType, data, args); - return { index, extractValue, type: numericType, data, pipes }; + return { index, extractValue, type: numericType, data, pipes, schema }; }); } @@ -252,12 +259,13 @@ export class WsContextCreator { data, metatype, pipes: paramPipes, + schema, } = param; const value = extractValue(...params); args[index] = await this.getParamValue( value, - { metatype, type, data }, + { metatype, type, data, schema } as ArgumentMetadata, pipes.concat(paramPipes), ); }; @@ -268,11 +276,11 @@ export class WsContextCreator { public async getParamValue( value: T, - { metatype, type, data }: { metatype: any; type: any; data: any }, + metadata: ArgumentMetadata, pipes: PipeTransform[], ): Promise { - return isEmpty(pipes) + return isEmptyArray(pipes) ? value - : this.pipesConsumer.apply(value, { metatype, type, data }, pipes); + : this.pipesConsumer.apply(value, metadata, pipes); } } diff --git a/packages/websockets/context/ws-metadata-constants.ts b/packages/websockets/context/ws-metadata-constants.ts index 76aee7e2d8d..7247980c685 100644 --- a/packages/websockets/context/ws-metadata-constants.ts +++ b/packages/websockets/context/ws-metadata-constants.ts @@ -1,4 +1,4 @@ -import { WsParamtype } from '../enums/ws-paramtype.enum'; +import { WsParamtype } from '../enums/ws-paramtype.enum.js'; export const DEFAULT_CALLBACK_METADATA = { [`${WsParamtype.ACK}:2`]: { index: 2, data: undefined, pipes: [] }, diff --git a/packages/websockets/context/ws-proxy.ts b/packages/websockets/context/ws-proxy.ts index dedbfddfc3e..be0306f0417 100644 --- a/packages/websockets/context/ws-proxy.ts +++ b/packages/websockets/context/ws-proxy.ts @@ -1,7 +1,7 @@ -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; import { EMPTY, isObservable } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { WsExceptionsHandler } from '../exceptions/ws-exceptions-handler'; +import { WsExceptionsHandler } from '../exceptions/ws-exceptions-handler.js'; +import { ExecutionContextHost } from '@nestjs/core/internal'; export class WsProxy { public create( diff --git a/packages/websockets/decorators/ack.decorator.ts b/packages/websockets/decorators/ack.decorator.ts index 728bc0964a8..b5aa93ab9a8 100644 --- a/packages/websockets/decorators/ack.decorator.ts +++ b/packages/websockets/decorators/ack.decorator.ts @@ -1,5 +1,5 @@ -import { WsParamtype } from '../enums/ws-paramtype.enum'; -import { createPipesWsParamDecorator } from '../utils/param.utils'; +import { WsParamtype } from '../enums/ws-paramtype.enum.js'; +import { createPipesWsParamDecorator } from '../utils/param.utils.js'; /** * WebSockets `ack` parameter decorator. diff --git a/packages/websockets/decorators/connected-socket.decorator.ts b/packages/websockets/decorators/connected-socket.decorator.ts index c21e4f8a15d..c43ac84e1ab 100644 --- a/packages/websockets/decorators/connected-socket.decorator.ts +++ b/packages/websockets/decorators/connected-socket.decorator.ts @@ -1,5 +1,5 @@ -import { WsParamtype } from '../enums/ws-paramtype.enum'; -import { createWsParamDecorator } from '../utils/param.utils'; +import { WsParamtype } from '../enums/ws-paramtype.enum.js'; +import { createWsParamDecorator } from '../utils/param.utils.js'; /** * @publicApi diff --git a/packages/websockets/decorators/gateway-server.decorator.ts b/packages/websockets/decorators/gateway-server.decorator.ts index 3a08326393a..2623603ef8c 100644 --- a/packages/websockets/decorators/gateway-server.decorator.ts +++ b/packages/websockets/decorators/gateway-server.decorator.ts @@ -1,4 +1,4 @@ -import { GATEWAY_SERVER_METADATA } from '../constants'; +import { GATEWAY_SERVER_METADATA } from '../constants.js'; /** * Attaches native Web Socket Server to a given property. diff --git a/packages/websockets/decorators/index.ts b/packages/websockets/decorators/index.ts index ee6f88bcd11..00e00c661e5 100644 --- a/packages/websockets/decorators/index.ts +++ b/packages/websockets/decorators/index.ts @@ -1,6 +1,6 @@ -export * from './connected-socket.decorator'; -export * from './gateway-server.decorator'; -export * from './message-body.decorator'; -export * from './socket-gateway.decorator'; -export * from './subscribe-message.decorator'; -export * from './ack.decorator'; +export * from './connected-socket.decorator.js'; +export * from './gateway-server.decorator.js'; +export * from './message-body.decorator.js'; +export * from './socket-gateway.decorator.js'; +export * from './subscribe-message.decorator.js'; +export * from './ack.decorator.js'; diff --git a/packages/websockets/decorators/message-body.decorator.ts b/packages/websockets/decorators/message-body.decorator.ts index 9b21e14fe3c..42d5fe84a1b 100644 --- a/packages/websockets/decorators/message-body.decorator.ts +++ b/packages/websockets/decorators/message-body.decorator.ts @@ -1,6 +1,10 @@ -import { PipeTransform, Type } from '@nestjs/common'; -import { WsParamtype } from '../enums/ws-paramtype.enum'; -import { createPipesWsParamDecorator } from '../utils/param.utils'; +import type { + ParameterDecoratorOptions, + PipeTransform, + Type, +} from '@nestjs/common'; +import { WsParamtype } from '../enums/ws-paramtype.enum.js'; +import { createPipesWsParamDecorator } from '../utils/param.utils.js'; /** * WebSockets message body parameter decorator. @@ -51,12 +55,51 @@ export function MessageBody( propertyKey: string, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator; +/** + * WebSockets message body parameter decorator. Extracts a property from the + * message payload object with additional options. + * + * For example, extracting a single param with schema: + * ```typescript + * create(@MessageBody('data', { schema: z.string() }) data: string) + * ``` + * @param propertyKey name of single property to extract from the message payload + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @publicApi + */ +export function MessageBody( + propertyKey: string, + options: ParameterDecoratorOptions, +): ParameterDecorator; +/** + * WebSockets message body parameter decorator. + * + * For example, passing schema as options: + * ```typescript + * create(@MessageBody({ schema: z.object({ data: z.string() }) }) body) + * ``` + * @param options options object containing additional configuration for the decorator, such as pipes and schema + * + * @publicApi + */ +export function MessageBody( + options: ParameterDecoratorOptions, +): ParameterDecorator; export function MessageBody( - propertyOrPipe?: string | (Type | PipeTransform), + propertyOrPipe?: + | string + | (Type | PipeTransform) + | ParameterDecoratorOptions, + optionsOrPipe?: + | ParameterDecoratorOptions + | Type + | PipeTransform, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator { return createPipesWsParamDecorator(WsParamtype.PAYLOAD)( propertyOrPipe, + optionsOrPipe, ...pipes, ); } diff --git a/packages/websockets/decorators/socket-gateway.decorator.ts b/packages/websockets/decorators/socket-gateway.decorator.ts index 8fc1c9baef1..d4e6f52fdb5 100644 --- a/packages/websockets/decorators/socket-gateway.decorator.ts +++ b/packages/websockets/decorators/socket-gateway.decorator.ts @@ -1,5 +1,9 @@ -import { GATEWAY_METADATA, GATEWAY_OPTIONS, PORT_METADATA } from '../constants'; -import { GatewayMetadata } from '../interfaces'; +import { + GATEWAY_METADATA, + GATEWAY_OPTIONS, + PORT_METADATA, +} from '../constants.js'; +import { GatewayMetadata } from '../interfaces/index.js'; /** * Decorator that marks a class as a Nest gateway that enables real-time, bidirectional diff --git a/packages/websockets/decorators/subscribe-message.decorator.ts b/packages/websockets/decorators/subscribe-message.decorator.ts index 4e332990024..807e013c528 100644 --- a/packages/websockets/decorators/subscribe-message.decorator.ts +++ b/packages/websockets/decorators/subscribe-message.decorator.ts @@ -1,4 +1,4 @@ -import { MESSAGE_MAPPING_METADATA, MESSAGE_METADATA } from '../constants'; +import { MESSAGE_MAPPING_METADATA, MESSAGE_METADATA } from '../constants.js'; /** * Subscribes to messages that fulfils chosen pattern. diff --git a/packages/websockets/enums/ws-paramtype.enum.ts b/packages/websockets/enums/ws-paramtype.enum.ts index 731aacc161b..5835780dc86 100644 --- a/packages/websockets/enums/ws-paramtype.enum.ts +++ b/packages/websockets/enums/ws-paramtype.enum.ts @@ -1,4 +1,4 @@ -import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum'; +import { RouteParamtypes } from '@nestjs/common/internal'; export enum WsParamtype { SOCKET = RouteParamtypes.REQUEST, diff --git a/packages/websockets/errors/index.ts b/packages/websockets/errors/index.ts index 83185f3a6ef..d6616c5872c 100644 --- a/packages/websockets/errors/index.ts +++ b/packages/websockets/errors/index.ts @@ -1 +1 @@ -export * from './ws-exception'; +export * from './ws-exception.js'; diff --git a/packages/websockets/errors/invalid-socket-port.exception.ts b/packages/websockets/errors/invalid-socket-port.exception.ts index 59f0b3faa57..dc73ccc0248 100644 --- a/packages/websockets/errors/invalid-socket-port.exception.ts +++ b/packages/websockets/errors/invalid-socket-port.exception.ts @@ -1,4 +1,4 @@ -import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception'; +import { RuntimeException } from '@nestjs/core/internal'; export class InvalidSocketPortException extends RuntimeException { constructor(port: number | string, type: any) { diff --git a/packages/websockets/errors/ws-exception.ts b/packages/websockets/errors/ws-exception.ts index f6eddd845de..52717997847 100644 --- a/packages/websockets/errors/ws-exception.ts +++ b/packages/websockets/errors/ws-exception.ts @@ -1,4 +1,4 @@ -import { isObject, isString } from '@nestjs/common/utils/shared.utils'; +import { isObject, isString } from '@nestjs/common/internal'; export class WsException extends Error { constructor(private readonly error: string | object) { diff --git a/packages/websockets/exceptions/base-ws-exception-filter.ts b/packages/websockets/exceptions/base-ws-exception-filter.ts index be46ec05da6..d006286fc63 100644 --- a/packages/websockets/exceptions/base-ws-exception-filter.ts +++ b/packages/websockets/exceptions/base-ws-exception-filter.ts @@ -1,12 +1,12 @@ import { - ArgumentsHost, + type ArgumentsHost, IntrinsicException, Logger, - WsExceptionFilter, + type WsExceptionFilter, } from '@nestjs/common'; -import { isObject } from '@nestjs/common/utils/shared.utils'; -import { MESSAGES } from '@nestjs/core/constants'; -import { WsException } from '../errors/ws-exception'; +import { WsException } from '../errors/ws-exception.js'; +import { isObject } from '@nestjs/common/internal'; +import { MESSAGES } from '@nestjs/core/internal'; export interface ErrorPayload { /** diff --git a/packages/websockets/exceptions/index.ts b/packages/websockets/exceptions/index.ts index 407cea5fbb0..27813896a4a 100644 --- a/packages/websockets/exceptions/index.ts +++ b/packages/websockets/exceptions/index.ts @@ -1 +1 @@ -export * from './base-ws-exception-filter'; +export * from './base-ws-exception-filter.js'; diff --git a/packages/websockets/exceptions/ws-exceptions-handler.ts b/packages/websockets/exceptions/ws-exceptions-handler.ts index 3efbbbfad58..4d71bdfe586 100644 --- a/packages/websockets/exceptions/ws-exceptions-handler.ts +++ b/packages/websockets/exceptions/ws-exceptions-handler.ts @@ -1,10 +1,12 @@ -import { ArgumentsHost } from '@nestjs/common'; -import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/exception-filter-metadata.interface'; -import { selectExceptionFilterMetadata } from '@nestjs/common/utils/select-exception-filter-metadata.util'; -import { isEmpty } from '@nestjs/common/utils/shared.utils'; -import { InvalidExceptionFilterException } from '@nestjs/core/errors/exceptions/invalid-exception-filter.exception'; -import { WsException } from '../errors/ws-exception'; -import { BaseWsExceptionFilter } from './base-ws-exception-filter'; +import type { ArgumentsHost } from '@nestjs/common'; +import { WsException } from '../errors/ws-exception.js'; +import { BaseWsExceptionFilter } from './base-ws-exception-filter.js'; +import { + type ExceptionFilterMetadata, + selectExceptionFilterMetadata, + isEmptyArray, +} from '@nestjs/common/internal'; +import { InvalidExceptionFilterException } from '@nestjs/core/internal'; /** * @publicApi @@ -31,7 +33,7 @@ export class WsExceptionsHandler extends BaseWsExceptionFilter { exception: T, args: ArgumentsHost, ): boolean { - if (isEmpty(this.filters)) return false; + if (isEmptyArray(this.filters)) return false; const filter = selectExceptionFilterMetadata(this.filters, exception); filter && filter.func(exception, args); diff --git a/packages/websockets/factories/server-and-event-streams-factory.ts b/packages/websockets/factories/server-and-event-streams-factory.ts index fc7038e5280..9f2e570a2b1 100644 --- a/packages/websockets/factories/server-and-event-streams-factory.ts +++ b/packages/websockets/factories/server-and-event-streams-factory.ts @@ -1,5 +1,5 @@ import { ReplaySubject, Subject } from 'rxjs'; -import { ServerAndEventStreamsHost } from '../interfaces/server-and-event-streams-host.interface'; +import { ServerAndEventStreamsHost } from '../interfaces/server-and-event-streams-host.interface.js'; export class ServerAndEventStreamsFactory { public static create(server: T): ServerAndEventStreamsHost { diff --git a/packages/websockets/factories/ws-params-factory.ts b/packages/websockets/factories/ws-params-factory.ts index 2ef6fefa001..039c8d9e97b 100644 --- a/packages/websockets/factories/ws-params-factory.ts +++ b/packages/websockets/factories/ws-params-factory.ts @@ -1,5 +1,5 @@ -import { isFunction } from '@nestjs/common/utils/shared.utils'; -import { WsParamtype } from '../enums/ws-paramtype.enum'; +import { WsParamtype } from '../enums/ws-paramtype.enum.js'; +import { isFunction } from '@nestjs/common/internal'; export class WsParamsFactory { public exchangeKeyForValue( diff --git a/packages/websockets/gateway-metadata-explorer.ts b/packages/websockets/gateway-metadata-explorer.ts index 66f44cc9981..069742fd483 100644 --- a/packages/websockets/gateway-metadata-explorer.ts +++ b/packages/websockets/gateway-metadata-explorer.ts @@ -1,16 +1,15 @@ -import { isFunction, isUndefined } from '@nestjs/common/utils/shared.utils'; -import { MetadataScanner } from '@nestjs/core/metadata-scanner'; import { Observable } from 'rxjs'; import { GATEWAY_SERVER_METADATA, MESSAGE_MAPPING_METADATA, MESSAGE_METADATA, PARAM_ARGS_METADATA, -} from './constants'; -import { NestGateway } from './interfaces/nest-gateway.interface'; -import { ParamsMetadata } from '@nestjs/core/helpers/interfaces'; -import { WsParamtype } from './enums/ws-paramtype.enum'; -import { ContextUtils } from '@nestjs/core/helpers/context-utils'; +} from './constants.js'; +import { NestGateway } from './interfaces/nest-gateway.interface.js'; +import { WsParamtype } from './enums/ws-paramtype.enum.js'; +import { isFunction, isUndefined } from '@nestjs/common/internal'; +import type { MetadataScanner } from '@nestjs/core'; +import { type ParamsMetadata, ContextUtils } from '@nestjs/core/internal'; export interface MessageMappingProperties { message: any; diff --git a/packages/websockets/index.ts b/packages/websockets/index.ts index c831a18b231..9e0948c5eae 100644 --- a/packages/websockets/index.ts +++ b/packages/websockets/index.ts @@ -6,9 +6,9 @@ */ import 'reflect-metadata'; -export * from './adapters'; -export * from './decorators'; -export * from './errors'; -export * from './exceptions'; -export { MessageMappingProperties } from './gateway-metadata-explorer'; -export * from './interfaces'; +export * from './adapters/index.js'; +export * from './decorators/index.js'; +export * from './errors/index.js'; +export * from './exceptions/index.js'; +export { MessageMappingProperties } from './gateway-metadata-explorer.js'; +export * from './interfaces/index.js'; diff --git a/packages/websockets/interfaces/gateway-metadata.interface.ts b/packages/websockets/interfaces/gateway-metadata.interface.ts index 7164c21a9ec..c9d556537b0 100644 --- a/packages/websockets/interfaces/gateway-metadata.interface.ts +++ b/packages/websockets/interfaces/gateway-metadata.interface.ts @@ -1,4 +1,4 @@ -import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface'; +import type { CorsOptions } from '@nestjs/common/internal'; /** * External interface diff --git a/packages/websockets/interfaces/hooks/index.ts b/packages/websockets/interfaces/hooks/index.ts index b80cb26ca49..3142711a01c 100644 --- a/packages/websockets/interfaces/hooks/index.ts +++ b/packages/websockets/interfaces/hooks/index.ts @@ -1,3 +1,3 @@ -export * from './on-gateway-connection.interface'; -export * from './on-gateway-disconnect.interface'; -export * from './on-gateway-init.interface'; +export * from './on-gateway-connection.interface.js'; +export * from './on-gateway-disconnect.interface.js'; +export * from './on-gateway-init.interface.js'; diff --git a/packages/websockets/interfaces/hooks/on-gateway-disconnect.interface.ts b/packages/websockets/interfaces/hooks/on-gateway-disconnect.interface.ts index e86e27d8fa7..728738ae4c5 100644 --- a/packages/websockets/interfaces/hooks/on-gateway-disconnect.interface.ts +++ b/packages/websockets/interfaces/hooks/on-gateway-disconnect.interface.ts @@ -2,5 +2,5 @@ * @publicApi */ export interface OnGatewayDisconnect { - handleDisconnect(client: T): any; + handleDisconnect(client: T, reason?: string): any; } diff --git a/packages/websockets/interfaces/index.ts b/packages/websockets/interfaces/index.ts index 80428a5fb6d..90ef3dbe556 100644 --- a/packages/websockets/interfaces/index.ts +++ b/packages/websockets/interfaces/index.ts @@ -1,5 +1,5 @@ -export * from './gateway-metadata.interface'; -export * from './hooks'; -export * from './server-and-event-streams-host.interface'; -export * from './web-socket-server.interface'; -export * from './ws-response.interface'; +export * from './gateway-metadata.interface.js'; +export * from './hooks/index.js'; +export * from './server-and-event-streams-host.interface.js'; +export * from './web-socket-server.interface.js'; +export * from './ws-response.interface.js'; diff --git a/packages/websockets/interfaces/nest-gateway.interface.ts b/packages/websockets/interfaces/nest-gateway.interface.ts index 89472d3626d..b5006c96e4a 100644 --- a/packages/websockets/interfaces/nest-gateway.interface.ts +++ b/packages/websockets/interfaces/nest-gateway.interface.ts @@ -4,5 +4,5 @@ export interface NestGateway { afterInit?: (server: any) => void; handleConnection?: (...args: any[]) => void; - handleDisconnect?: (client: any) => void; + handleDisconnect?: (client: any, reason?: string) => void; } diff --git a/packages/websockets/internal.ts b/packages/websockets/internal.ts new file mode 100644 index 00000000000..2e8a895ed4a --- /dev/null +++ b/packages/websockets/internal.ts @@ -0,0 +1,9 @@ +/** + * Internal module - not part of the public API. + * These exports are used by sibling @nestjs packages. + * Do not depend on these in your application code. + * @internal + * @module + */ + +export * from './constants.js'; diff --git a/packages/websockets/package.json b/packages/websockets/package.json index 0ab580c8d06..575ec23fa5e 100644 --- a/packages/websockets/package.json +++ b/packages/websockets/package.json @@ -4,6 +4,14 @@ "description": "Nest - modern, fast, powerful node.js web framework (@websockets)", "author": "Kamil Mysliwiec", "license": "MIT", + "type": "module", + "main": "./index.js", + "exports": { + ".": "./index.js", + "./internal": "./internal.js", + "./*.js": "./*.js", + "./*": "./*.js" + }, "repository": { "type": "git", "url": "https://github.com/nestjs/nest.git", diff --git a/packages/websockets/socket-module.ts b/packages/websockets/socket-module.ts index c0fe93f93c2..2ffd38e6dd4 100644 --- a/packages/websockets/socket-module.ts +++ b/packages/websockets/socket-module.ts @@ -1,28 +1,34 @@ -import { NestApplicationOptions } from '@nestjs/common'; -import { InjectionToken } from '@nestjs/common/interfaces'; -import { Injectable } from '@nestjs/common/interfaces/injectable.interface'; -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { GuardsConsumer } from '@nestjs/core/guards/guards-consumer'; -import { GuardsContextCreator } from '@nestjs/core/guards/guards-context-creator'; -import { loadAdapter } from '@nestjs/core/helpers/load-adapter'; -import { NestContainer } from '@nestjs/core/injector/container'; -import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; -import { InterceptorsConsumer } from '@nestjs/core/interceptors/interceptors-consumer'; -import { InterceptorsContextCreator } from '@nestjs/core/interceptors/interceptors-context-creator'; -import { PipesConsumer } from '@nestjs/core/pipes/pipes-consumer'; -import { PipesContextCreator } from '@nestjs/core/pipes/pipes-context-creator'; +import type { NestApplicationOptions } from '@nestjs/common'; import { iterate } from 'iterare'; -import { AbstractWsAdapter } from './adapters'; -import { GATEWAY_METADATA } from './constants'; -import { ExceptionFiltersContext } from './context/exception-filters-context'; -import { WsContextCreator } from './context/ws-context-creator'; -import { WsProxy } from './context/ws-proxy'; -import { NestGateway } from './interfaces/nest-gateway.interface'; -import { SocketServerProvider } from './socket-server-provider'; -import { SocketsContainer } from './sockets-container'; -import { WebSocketsController } from './web-sockets-controller'; +import { AbstractWsAdapter } from './adapters/index.js'; +import { GATEWAY_METADATA } from './constants.js'; +import { ExceptionFiltersContext } from './context/exception-filters-context.js'; +import { WsContextCreator } from './context/ws-context-creator.js'; +import { WsProxy } from './context/ws-proxy.js'; +import { NestGateway } from './interfaces/nest-gateway.interface.js'; +import { SocketServerProvider } from './socket-server-provider.js'; +import { SocketsContainer } from './sockets-container.js'; +import { WebSocketsController } from './web-sockets-controller.js'; +import type { InjectionToken } from '@nestjs/common'; +import type { + Injectable, + NestApplicationContextOptions, +} from '@nestjs/common/internal'; +import type { + ApplicationConfig, + NestContainer, + GraphInspector, +} from '@nestjs/core'; +import { + GuardsConsumer, + GuardsContextCreator, + loadAdapter, + type InstanceWrapper, + InterceptorsConsumer, + InterceptorsContextCreator, + PipesConsumer, + PipesContextCreator, +} from '@nestjs/core/internal'; export class SocketModule< THttpServer = any, @@ -74,7 +80,7 @@ export class SocketModule< .forEach(wrapper => this.connectGatewayToServer(wrapper, moduleName)); } - public connectGatewayToServer( + public async connectGatewayToServer( wrapper: InstanceWrapper, moduleName: string, ) { @@ -84,7 +90,7 @@ export class SocketModule< return; } if (!this.isAdapterInitialized) { - this.initializeAdapter(); + await this.initializeAdapter(); } this.webSocketsController.connectGatewayToServer( instance as NestGateway, @@ -113,7 +119,7 @@ export class SocketModule< this.socketsContainer.clear(); } - private initializeAdapter() { + private async initializeAdapter() { const forceCloseConnections = (this.appOptions as NestApplicationOptions) .forceCloseConnections; const adapter = this.applicationConfig.getIoAdapter(); @@ -123,10 +129,10 @@ export class SocketModule< this.isAdapterInitialized = true; return; } - const { IoAdapter } = loadAdapter( + const { IoAdapter } = await loadAdapter( '@nestjs/platform-socket.io', 'WebSockets', - () => require('@nestjs/platform-socket.io'), + () => import('@nestjs/platform-socket.io'), ); const ioAdapter = new IoAdapter(this.httpServer); ioAdapter.forceCloseConnections = forceCloseConnections; diff --git a/packages/websockets/socket-server-provider.ts b/packages/websockets/socket-server-provider.ts index a4b54f4533f..70a84815c6e 100644 --- a/packages/websockets/socket-server-provider.ts +++ b/packages/websockets/socket-server-provider.ts @@ -1,9 +1,9 @@ -import { addLeadingSlash, isString } from '@nestjs/common/utils/shared.utils'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { ServerAndEventStreamsFactory } from './factories/server-and-event-streams-factory'; -import { GatewayMetadata } from './interfaces/gateway-metadata.interface'; -import { ServerAndEventStreamsHost } from './interfaces/server-and-event-streams-host.interface'; -import { SocketsContainer } from './sockets-container'; +import { ServerAndEventStreamsFactory } from './factories/server-and-event-streams-factory.js'; +import { GatewayMetadata } from './interfaces/gateway-metadata.interface.js'; +import { ServerAndEventStreamsHost } from './interfaces/server-and-event-streams-host.interface.js'; +import { SocketsContainer } from './sockets-container.js'; +import { addLeadingSlash, isString } from '@nestjs/common/internal'; +import type { ApplicationConfig } from '@nestjs/core'; export class SocketServerProvider { constructor( diff --git a/packages/websockets/sockets-container.ts b/packages/websockets/sockets-container.ts index f6b0ccfccf3..763bdc85b7f 100644 --- a/packages/websockets/sockets-container.ts +++ b/packages/websockets/sockets-container.ts @@ -1,5 +1,8 @@ -import * as hash from 'object-hash'; -import { GatewayMetadata, ServerAndEventStreamsHost } from './interfaces'; +import hash from 'object-hash'; +import { + GatewayMetadata, + ServerAndEventStreamsHost, +} from './interfaces/index.js'; export class SocketsContainer { private readonly serverAndEventStreamsHosts = new Map< diff --git a/packages/websockets/test/container.spec.ts b/packages/websockets/test/container.spec.ts index 1947804f5dc..82997bec93b 100644 --- a/packages/websockets/test/container.spec.ts +++ b/packages/websockets/test/container.spec.ts @@ -1,17 +1,15 @@ -import { expect } from 'chai'; -import * as hash from 'object-hash'; -import * as sinon from 'sinon'; -import { SocketsContainer } from '../sockets-container'; +import hash from 'object-hash'; +import { SocketsContainer } from '../sockets-container.js'; describe('SocketsContainer', () => { const port = 30; let instance: SocketsContainer; - let getSpy: sinon.SinonSpy, setSpy: sinon.SinonSpy; + let getSpy: ReturnType, setSpy: ReturnType; beforeEach(() => { - setSpy = sinon.spy(); - getSpy = sinon.spy(); + setSpy = vi.fn(); + getSpy = vi.fn(); instance = new SocketsContainer(); (instance as any).serverAndEventStreamsHosts = { get: getSpy, @@ -24,7 +22,7 @@ describe('SocketsContainer', () => { instance.getOneByConfig(config); const token = hash(config); - expect(getSpy.calledWith(token)).to.be.true; + expect(getSpy).toHaveBeenCalledWith(token); }); }); describe('addOne', () => { @@ -35,22 +33,22 @@ describe('SocketsContainer', () => { instance.addOne(config, server as any); const token = hash(config); - expect(setSpy.calledWith(token, server)).to.be.true; + expect(setSpy).toHaveBeenCalledWith(token, server); }); }); describe('getAll', () => { it('should return "serverAndEventStreamsHosts"', () => { const collection = ['test']; (instance as any).serverAndEventStreamsHosts = collection; - expect(instance.getAll()).to.be.eq(collection); + expect(instance.getAll()).toBe(collection); }); }); describe('clear', () => { it('should clear hosts collection', () => { - const collection = { clear: sinon.spy() }; + const collection = { clear: vi.fn() }; (instance as any).serverAndEventStreamsHosts = collection; instance.clear(); - expect(collection.clear.called).to.be.true; + expect(collection.clear).toHaveBeenCalled(); }); }); }); diff --git a/packages/websockets/test/context/exception-filters.context.spec.ts b/packages/websockets/test/context/exception-filters.context.spec.ts index d58730c1e35..80c95db1bdc 100644 --- a/packages/websockets/test/context/exception-filters.context.spec.ts +++ b/packages/websockets/test/context/exception-filters.context.spec.ts @@ -1,9 +1,7 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { Catch } from '../../../common/decorators/core/catch.decorator'; -import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator'; -import { NestContainer } from '../../../core/injector/container'; -import { ExceptionFiltersContext } from '../../context/exception-filters-context'; +import { Catch } from '../../../common/decorators/core/catch.decorator.js'; +import { UseFilters } from '../../../common/decorators/core/exception-filters.decorator.js'; +import { NestContainer } from '../../../core/injector/container.js'; +import { ExceptionFiltersContext } from '../../context/exception-filters-context.js'; describe('ExceptionFiltersContext', () => { let exceptionFilter: ExceptionFiltersContext; @@ -21,7 +19,7 @@ describe('ExceptionFiltersContext', () => { describe('when filters metadata is empty', () => { class EmptyMetadata {} beforeEach(() => { - sinon.stub(exceptionFilter, 'createContext').returns([]); + vi.spyOn(exceptionFilter, 'createContext').mockReturnValue([]); }); it('should return plain ExceptionHandler object', () => { const filter = exceptionFilter.create( @@ -29,7 +27,7 @@ describe('ExceptionFiltersContext', () => { () => ({}) as any, '', ); - expect((filter as any).filters).to.be.empty; + expect((filter as any).filters).toHaveLength(0); }); }); describe('when filters metadata is not empty', () => { @@ -42,7 +40,7 @@ describe('ExceptionFiltersContext', () => { () => ({}) as any, '', ); - expect((filter as any).filters).to.not.be.empty; + expect((filter as any).filters).not.toHaveLength(0); }); }); }); diff --git a/packages/websockets/test/context/ws-context-creator.spec.ts b/packages/websockets/test/context/ws-context-creator.spec.ts index 37b0b28359f..bef98b92f82 100644 --- a/packages/websockets/test/context/ws-context-creator.spec.ts +++ b/packages/websockets/test/context/ws-context-creator.spec.ts @@ -1,22 +1,20 @@ -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; import { of } from 'rxjs'; -import * as sinon from 'sinon'; -import { Injectable, UseGuards, UsePipes } from '../../../common'; -import { CUSTOM_ROUTE_ARGS_METADATA } from '../../../common/constants'; -import { GuardsConsumer } from '../../../core/guards/guards-consumer'; -import { GuardsContextCreator } from '../../../core/guards/guards-context-creator'; -import { NestContainer } from '../../../core/injector/container'; -import { InterceptorsConsumer } from '../../../core/interceptors/interceptors-consumer'; -import { InterceptorsContextCreator } from '../../../core/interceptors/interceptors-context-creator'; -import { PipesConsumer } from '../../../core/pipes/pipes-consumer'; -import { PipesContextCreator } from '../../../core/pipes/pipes-context-creator'; -import { ExceptionFiltersContext } from '../../context/exception-filters-context'; -import { WsContextCreator } from '../../context/ws-context-creator'; -import { WsProxy } from '../../context/ws-proxy'; -import { WsParamtype } from '../../enums/ws-paramtype.enum'; -import { WsParamsFactory } from '../../factories/ws-params-factory'; -import { WsException } from '../../index'; +import { CUSTOM_ROUTE_ARGS_METADATA } from '../../../common/constants.js'; +import { Injectable, UseGuards, UsePipes } from '../../../common/index.js'; +import { GuardsConsumer } from '../../../core/guards/guards-consumer.js'; +import { GuardsContextCreator } from '../../../core/guards/guards-context-creator.js'; +import { NestContainer } from '../../../core/injector/container.js'; +import { InterceptorsConsumer } from '../../../core/interceptors/interceptors-consumer.js'; +import { InterceptorsContextCreator } from '../../../core/interceptors/interceptors-context-creator.js'; +import { PipesConsumer } from '../../../core/pipes/pipes-consumer.js'; +import { PipesContextCreator } from '../../../core/pipes/pipes-context-creator.js'; +import { ExceptionFiltersContext } from '../../context/exception-filters-context.js'; +import { WsContextCreator } from '../../context/ws-context-creator.js'; +import { WsProxy } from '../../context/ws-proxy.js'; +import { WsParamtype } from '../../enums/ws-paramtype.enum.js'; +import { WsParamsFactory } from '../../factories/ws-params-factory.js'; +import { WsException } from '../../index.js'; @Injectable() class TestGuard { @@ -52,7 +50,7 @@ describe('WsContextCreator', () => { beforeEach(() => { wsProxy = new WsProxy(); - sinon.stub(wsProxy, 'create').callsFake(a => a); + vi.spyOn(wsProxy, 'create').mockImplementation(a => a); exceptionFiltersContext = new ExceptionFiltersContext( new NestContainer() as any, @@ -77,30 +75,38 @@ describe('WsContextCreator', () => { }); describe('create', () => { it('should create exception handler', () => { - const handlerCreateSpy = sinon.spy(exceptionFiltersContext, 'create'); + const handlerCreateSpy = vi.spyOn(exceptionFiltersContext, 'create'); contextCreator.create(instance, instance.test, module, 'create'); - expect( - handlerCreateSpy.calledWith(instance, instance.test as any, module), - ).to.be.true; + expect(handlerCreateSpy).toHaveBeenCalledWith( + instance, + instance.test as any, + module, + ); }); it('should create pipes context', () => { - const pipesCreateSpy = sinon.spy(pipesCreator, 'create'); + const pipesCreateSpy = vi.spyOn(pipesCreator, 'create'); contextCreator.create(instance, instance.test, module, 'create'); - expect(pipesCreateSpy.calledWith(instance, instance.test, module)).to.be - .true; + expect(pipesCreateSpy).toHaveBeenCalledWith( + instance, + instance.test, + module, + ); }); it('should create guards context', () => { - const guardsCreateSpy = sinon.spy(guardsContextCreator, 'create'); + const guardsCreateSpy = vi.spyOn(guardsContextCreator, 'create'); contextCreator.create(instance, instance.test, module, 'create'); - expect(guardsCreateSpy.calledWith(instance, instance.test, module)).to.be - .true; + expect(guardsCreateSpy).toHaveBeenCalledWith( + instance, + instance.test, + module, + ); }); describe('when proxy called', () => { it('should call guards consumer `tryActivate`', async () => { - const tryActivateSpy = sinon.spy(guardsConsumer, 'tryActivate'); - sinon - .stub(guardsContextCreator, 'create') - .callsFake(() => [{ canActivate: () => true }]); + const tryActivateSpy = vi.spyOn(guardsConsumer, 'tryActivate'); + vi.spyOn(guardsContextCreator, 'create').mockImplementation( + () => [{ canActivate: () => true }] as any, + ); const proxy = contextCreator.create( instance, instance.test, @@ -110,13 +116,13 @@ describe('WsContextCreator', () => { const data = 'test'; await proxy(null, data); - expect(tryActivateSpy.called).to.be.true; + expect(tryActivateSpy).toHaveBeenCalled(); }); describe('when can not activate', () => { it('should throw forbidden exception', () => { - sinon - .stub(guardsConsumer, 'tryActivate') - .callsFake(async () => false); + vi.spyOn(guardsConsumer, 'tryActivate').mockImplementation( + async () => false, + ); const proxy = contextCreator.create( instance, instance.test, @@ -125,7 +131,7 @@ describe('WsContextCreator', () => { ); const data = 'test'; proxy(null, data).catch(err => - expect(err).to.be.instanceOf(WsException), + expect(err).toBeInstanceOf(WsException), ); }); }); @@ -138,15 +144,17 @@ describe('WsContextCreator', () => { instance, instance.test, ); - expect(paramtypes).to.be.eql([String, Number]); + expect(paramtypes).toEqual([String, Number]); }); }); describe('createGuardsFn', () => { it('should throw exception when "tryActivate" returns false', () => { const guardsFn = contextCreator.createGuardsFn([null], null!, null!)!; - sinon.stub(guardsConsumer, 'tryActivate').callsFake(async () => false); - guardsFn([]).catch(err => expect(err).to.not.be.undefined); + vi.spyOn(guardsConsumer, 'tryActivate').mockImplementation( + async () => false, + ); + guardsFn([]).catch(err => expect(err).not.toBeUndefined()); }); }); @@ -174,18 +182,18 @@ describe('WsContextCreator', () => { { index: 2, type: WsParamtype.PAYLOAD, data: 'test' }, { index: 3, type: `key${CUSTOM_ROUTE_ARGS_METADATA}`, data: 'custom' }, ]; - expect(values[0]).to.deep.include(expectedValues[0]); - expect(values[1]).to.deep.include(expectedValues[1]); + expect(values[0]).toMatchObject(expectedValues[0]); + expect(values[1]).toMatchObject(expectedValues[1]); }); }); describe('getParamValue', () => { - let consumerApplySpy: sinon.SinonSpy; + let consumerApplySpy: ReturnType; const value = 3, metatype = null, - transforms = [{ transform: sinon.spy() }]; + transforms = [{ transform: vi.fn() }]; beforeEach(() => { - consumerApplySpy = sinon.spy(pipesConsumer, 'apply'); + consumerApplySpy = vi.spyOn(pipesConsumer, 'apply'); }); it('should call "consumer.apply"', async () => { await contextCreator.getParamValue( @@ -193,14 +201,14 @@ describe('WsContextCreator', () => { { metatype, type: WsParamtype.PAYLOAD, data: null }, transforms, ); - expect(consumerApplySpy.called).to.be.true; + expect(consumerApplySpy).toHaveBeenCalled(); }); }); describe('createPipesFn', () => { describe('when "paramsOptions" is empty', () => { it('returns null', async () => { const pipesFn = contextCreator.createPipesFn([], []); - expect(pipesFn).to.be.null; + expect(pipesFn).toBeNull(); }); }); describe('when "paramsOptions" is not empty', () => { @@ -218,7 +226,7 @@ describe('WsContextCreator', () => { ], )!; await pipesFn([]); - expect(pipesFn).to.be.a('function'); + expect(pipesFn).toBeTypeOf('function'); }); }); }); diff --git a/packages/websockets/test/context/ws-proxy.spec.ts b/packages/websockets/test/context/ws-proxy.spec.ts index 7266ce13b32..63ad43f7a14 100644 --- a/packages/websockets/test/context/ws-proxy.spec.ts +++ b/packages/websockets/test/context/ws-proxy.spec.ts @@ -1,42 +1,46 @@ -import { expect } from 'chai'; import { throwError } from 'rxjs'; -import * as sinon from 'sinon'; -import { WsProxy } from '../../context/ws-proxy'; -import { WsException } from '../../errors/ws-exception'; -import { WsExceptionsHandler } from '../../exceptions/ws-exceptions-handler'; +import { WsProxy } from '../../context/ws-proxy.js'; +import { WsException } from '../../errors/ws-exception.js'; +import { WsExceptionsHandler } from '../../exceptions/ws-exceptions-handler.js'; describe('WsProxy', () => { let routerProxy: WsProxy; - let handlerMock: sinon.SinonMock; let handler: WsExceptionsHandler; beforeEach(() => { handler = new WsExceptionsHandler(); - handlerMock = sinon.mock(handler); routerProxy = new WsProxy(); }); describe('create', () => { it('should method return thunk', () => { const proxy = routerProxy.create(async (client, data) => {}, handler); - expect(typeof proxy === 'function').to.be.true; + expect(typeof proxy === 'function').toBe(true); }); it('should method encapsulate callback passed as argument', async () => { - const expectation = handlerMock.expects('handle').once(); + const handleSpy = vi + .spyOn(handler, 'handle') + .mockImplementation(() => {}); const proxy = routerProxy.create(async (client, data) => { throw new WsException('test'); }, handler); await proxy(null, null); - expectation.verify(); + expect(handleSpy).toHaveBeenCalledOnce(); }); it('should attach "catchError" operator when observable was returned', async () => { - const expectation = handlerMock.expects('handle').once(); + const handleSpy = vi + .spyOn(handler, 'handle') + .mockImplementation(() => {}); const proxy = routerProxy.create(async (client, data) => { return throwError(() => new WsException('test')); }, handler); - (await proxy(null, null)).subscribe(null, () => expectation.verify()); + (await proxy(null, null)).subscribe({ + error: () => { + expect(handleSpy).toHaveBeenCalledOnce(); + }, + }); }); }); }); diff --git a/packages/websockets/test/decorators/ack.decorator.spec.ts b/packages/websockets/test/decorators/ack.decorator.spec.ts index 8bcc737e81e..b3fe49c55b0 100644 --- a/packages/websockets/test/decorators/ack.decorator.spec.ts +++ b/packages/websockets/test/decorators/ack.decorator.spec.ts @@ -1,8 +1,7 @@ import 'reflect-metadata'; -import { expect } from 'chai'; -import { PARAM_ARGS_METADATA } from '../../constants'; -import { Ack } from '../../decorators/ack.decorator'; -import { WsParamtype } from '../../enums/ws-paramtype.enum'; +import { PARAM_ARGS_METADATA } from '../../constants.js'; +import { Ack } from '../../decorators/ack.decorator.js'; +import { WsParamtype } from '../../enums/ws-paramtype.enum.js'; class AckTest { public test(@Ack() ack: Function) {} @@ -23,6 +22,6 @@ describe('@Ack', () => { pipes: [], }, }; - expect(argsMetadata).to.be.eql(expectedMetadata); + expect(argsMetadata).toEqual(expectedMetadata); }); }); diff --git a/packages/websockets/test/decorators/connected-socket.decorator.spec.ts b/packages/websockets/test/decorators/connected-socket.decorator.spec.ts index 7d92ee57a3a..6ce9f477db8 100644 --- a/packages/websockets/test/decorators/connected-socket.decorator.spec.ts +++ b/packages/websockets/test/decorators/connected-socket.decorator.spec.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { PARAM_ARGS_METADATA } from '../../constants'; -import { ConnectedSocket } from '../../decorators'; -import { WsParamtype } from '../../enums/ws-paramtype.enum'; +import { PARAM_ARGS_METADATA } from '../../constants.js'; +import { ConnectedSocket } from '../../decorators/index.js'; +import { WsParamtype } from '../../enums/ws-paramtype.enum.js'; class ConnectedSocketTest { public test(@ConnectedSocket() socket: any) {} @@ -21,6 +20,6 @@ describe('@ConnectedSocket', () => { pipes: [], }, }; - expect(argsMetadata).to.be.eql(expectedMetadata); + expect(argsMetadata).toEqual(expectedMetadata); }); }); diff --git a/packages/websockets/test/decorators/message-body.decorator.spec.ts b/packages/websockets/test/decorators/message-body.decorator.spec.ts index a379fed9564..840b5b4bb37 100644 --- a/packages/websockets/test/decorators/message-body.decorator.spec.ts +++ b/packages/websockets/test/decorators/message-body.decorator.spec.ts @@ -1,8 +1,7 @@ import { ValidationPipe } from '@nestjs/common'; -import { expect } from 'chai'; -import { PARAM_ARGS_METADATA } from '../../constants'; -import { MessageBody } from '../../decorators'; -import { WsParamtype } from '../../enums/ws-paramtype.enum'; +import { PARAM_ARGS_METADATA } from '../../constants.js'; +import { MessageBody } from '../../decorators/index.js'; +import { WsParamtype } from '../../enums/ws-paramtype.enum.js'; class MessagePayloadTest { public test(@MessageBody(ValidationPipe) payload: any) {} @@ -22,6 +21,71 @@ describe('@MessagePayload', () => { pipes: [ValidationPipe], }, }; - expect(argsMetadata).to.be.eql(expectedMetadata); + expect(argsMetadata).toEqual(expectedMetadata); + }); +}); + +describe('@MessageBody with ParameterDecoratorOptions', () => { + const mockSchema = { + '~standard': { + version: 1 as const, + vendor: 'test', + validate: (v: unknown) => ({ value: v }), + }, + }; + + it('should enhance param with schema when options passed as the only argument', () => { + class Test { + public test(@MessageBody({ schema: mockSchema }) body: any) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [], + schema: mockSchema, + }); + }); + + it('should enhance param with pipes when options with pipes passed as the only argument', () => { + class Test { + public test( + @MessageBody({ schema: mockSchema, pipes: [ValidationPipe] }) body: any, + ) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: undefined, + pipes: [ValidationPipe], + schema: mockSchema, + }); + }); + + it('should enhance param with schema when options passed as second argument with property', () => { + class Test { + public test(@MessageBody('data', { schema: mockSchema }) data: any) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key]).toEqual({ + index: 0, + data: 'data', + pipes: [], + schema: mockSchema, + }); + }); + + it('should not confuse a pipe instance with options', () => { + class Test { + public test(@MessageBody(new ValidationPipe()) body: any) {} + } + const metadata = Reflect.getMetadata(PARAM_ARGS_METADATA, Test, 'test'); + const key = Object.keys(metadata)[0]; + expect(metadata[key].data).toBeUndefined(); + expect(metadata[key].pipes).toHaveLength(1); + expect(metadata[key].schema).toBeUndefined(); }); }); diff --git a/packages/websockets/test/exceptions/ws-exception.spec.ts b/packages/websockets/test/exceptions/ws-exception.spec.ts index b058edbea00..e613d892576 100644 --- a/packages/websockets/test/exceptions/ws-exception.spec.ts +++ b/packages/websockets/test/exceptions/ws-exception.spec.ts @@ -1,5 +1,4 @@ -import { expect } from 'chai'; -import { WsException } from '../../errors/ws-exception'; +import { WsException } from '../../errors/ws-exception.js'; describe('WsException', () => { describe('when string passed', () => { @@ -7,10 +6,10 @@ describe('WsException', () => { const instance = new WsException(error); it('should return error message as string', () => { - expect(instance.getError()).to.be.eql(error); + expect(instance.getError()).toEqual(error); }); it('should set the message property', () => { - expect(instance.message).to.be.eql(error); + expect(instance.message).toEqual(error); }); }); @@ -20,10 +19,10 @@ describe('WsException', () => { const instance = new WsException(error); it('should return error as object', () => { - expect(instance.getError()).to.be.eql(error); + expect(instance.getError()).toEqual(error); }); it('should fallback error message to class name', () => { - expect(instance.message).to.be.eql('Ws Exception'); + expect(instance.message).toEqual('Ws Exception'); }); }); describe('and message property is not undefined', () => { @@ -31,10 +30,10 @@ describe('WsException', () => { const instance = new WsException(error); it('should return error as object', () => { - expect(instance.getError()).to.be.eql(error); + expect(instance.getError()).toEqual(error); }); it('should return error message as the extracted "message" string', () => { - expect(instance.message).to.be.eql(error.message); + expect(instance.message).toEqual(error.message); }); }); }); diff --git a/packages/websockets/test/exceptions/ws-exceptions-handler.spec.ts b/packages/websockets/test/exceptions/ws-exceptions-handler.spec.ts index c1b54ba4a2c..3df991bafaa 100644 --- a/packages/websockets/test/exceptions/ws-exceptions-handler.spec.ts +++ b/packages/websockets/test/exceptions/ws-exceptions-handler.spec.ts @@ -1,14 +1,12 @@ -import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { WsException } from '../../errors/ws-exception'; -import { WsExceptionsHandler } from '../../exceptions/ws-exceptions-handler'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host.js'; +import { WsException } from '../../errors/ws-exception.js'; +import { WsExceptionsHandler } from '../../exceptions/ws-exceptions-handler.js'; describe('WsExceptionsHandler', () => { let handler: WsExceptionsHandler; - let emitStub: sinon.SinonStub; + let emitStub: ReturnType; let client: { - emit: sinon.SinonStub; + emit: ReturnType; }; let pattern: string; let data: unknown; @@ -16,7 +14,7 @@ describe('WsExceptionsHandler', () => { beforeEach(() => { handler = new WsExceptionsHandler(); - emitStub = sinon.stub(); + emitStub = vi.fn(); client = { emit: emitStub, }; @@ -24,23 +22,21 @@ describe('WsExceptionsHandler', () => { data = { foo: 'bar' }; executionContextHost = new ExecutionContextHost([client, data, pattern]); - client.emit.returns(client); + client.emit.mockReturnValue(client); }); describe('handle', () => { describe('when "includeCause" is set to true (default)', () => { it('should method emit expected status code message when exception is unknown', () => { handler.handle(new Error(), executionContextHost); - expect( - emitStub.calledWith('exception', { - status: 'error', - message: 'Internal server error', - cause: { - pattern, - data, - }, - }), - ).to.be.true; + expect(emitStub).toHaveBeenCalledWith('exception', { + status: 'error', + message: 'Internal server error', + cause: { + pattern, + data, + }, + }); }); describe('when exception is instance of WsException', () => { it('should method emit expected status and json object', () => { @@ -48,23 +44,21 @@ describe('WsExceptionsHandler', () => { custom: 'Unauthorized', }; handler.handle(new WsException(message), executionContextHost); - expect(emitStub.calledWith('exception', message)).to.be.true; + expect(emitStub).toHaveBeenCalledWith('exception', message); }); it('should method emit expected status and transform message to json', () => { const message = 'Unauthorized'; handler.handle(new WsException(message), executionContextHost); - console.log(emitStub.getCall(0).args); - expect( - emitStub.calledWith('exception', { - message, - status: 'error', - cause: { - pattern, - data, - }, - }), - ).to.be.true; + console.log(emitStub.mock.calls[0]); + expect(emitStub).toHaveBeenCalledWith('exception', { + message, + status: 'error', + cause: { + pattern, + data, + }, + }); }); }); }); @@ -79,12 +73,10 @@ describe('WsExceptionsHandler', () => { new Error(), new ExecutionContextHost([client, pattern, data]), ); - expect( - emitStub.calledWith('exception', { - status: 'error', - message: 'Internal server error', - }), - ).to.be.true; + expect(emitStub).toHaveBeenCalledWith('exception', { + status: 'error', + message: 'Internal server error', + }); }); describe('when exception is instance of WsException', () => { it('should method emit expected status and json object', () => { @@ -92,25 +84,27 @@ describe('WsExceptionsHandler', () => { custom: 'Unauthorized', }; handler.handle(new WsException(message), executionContextHost); - expect(emitStub.calledWith('exception', message)).to.be.true; + expect(emitStub).toHaveBeenCalledWith('exception', message); }); it('should method emit expected status and transform message to json', () => { const message = 'Unauthorized'; handler.handle(new WsException(message), executionContextHost); - expect(emitStub.calledWith('exception', { message, status: 'error' })) - .to.be.true; + expect(emitStub).toHaveBeenCalledWith('exception', { + message, + status: 'error', + }); }); }); }); describe('when "invokeCustomFilters" returns true', () => { beforeEach(() => { - sinon.stub(handler, 'invokeCustomFilters').returns(true); + vi.spyOn(handler, 'invokeCustomFilters').mockReturnValue(true); }); it('should not call `emit`', () => { handler.handle(new WsException(''), executionContextHost); - expect(emitStub.notCalled).to.be.true; + expect(emitStub).not.toHaveBeenCalled(); }); }); }); @@ -118,24 +112,24 @@ describe('WsExceptionsHandler', () => { const filters = ['test', 'test2']; it('should set custom filters', () => { handler.setCustomFilters(filters as any); - expect((handler as any).filters).to.be.eql(filters); + expect((handler as any).filters).toEqual(filters); }); it('should throw exception when passed argument is not an array', () => { - expect(() => handler.setCustomFilters(null!)).to.throw(); + expect(() => handler.setCustomFilters(null!)).toThrow(); }); }); describe('invokeCustomFilters', () => { describe('when filters array is empty', () => { it('should return false', () => { - expect(handler.invokeCustomFilters(null, null!)).to.be.false; + expect(handler.invokeCustomFilters(null, null!)).toBe(false); }); }); describe('when filters array is not empty', () => { - let filters: any[], funcSpy: sinon.SinonSpy; + let filters: any[], funcSpy: ReturnType; class TestException {} beforeEach(() => { - funcSpy = sinon.spy(); + funcSpy = vi.fn(); }); describe('when filter exists in filters array', () => { beforeEach(() => { @@ -144,28 +138,30 @@ describe('WsExceptionsHandler', () => { }); it('should call funcSpy', () => { handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.false; + expect(funcSpy).toHaveBeenCalled(); }); it('should call funcSpy with exception and response passed as an arguments', () => { const exception = new TestException(); const res = { foo: 'bar' }; handler.invokeCustomFilters(exception, res as any); - expect(funcSpy.calledWith(exception, res)).to.be.true; + expect(funcSpy).toHaveBeenCalledWith(exception, res); }); it('should return true', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .true; + expect(handler.invokeCustomFilters(new TestException(), null!)).toBe( + true, + ); }); }); describe('when filter does not exists in filters array', () => { it('should not call funcSpy', () => { handler.invokeCustomFilters(new TestException(), null!); - expect(funcSpy.notCalled).to.be.true; + expect(funcSpy).not.toHaveBeenCalled(); }); it('should return false', () => { - expect(handler.invokeCustomFilters(new TestException(), null!)).to.be - .false; + expect(handler.invokeCustomFilters(new TestException(), null!)).toBe( + false, + ); }); }); }); diff --git a/packages/websockets/test/factories/server-and-event-streams-factory.spec.ts b/packages/websockets/test/factories/server-and-event-streams-factory.spec.ts index 7d7e148cb0f..45ae35b2caf 100644 --- a/packages/websockets/test/factories/server-and-event-streams-factory.spec.ts +++ b/packages/websockets/test/factories/server-and-event-streams-factory.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; import { ReplaySubject, Subject } from 'rxjs'; -import { ServerAndEventStreamsFactory } from '../../factories/server-and-event-streams-factory'; +import { ServerAndEventStreamsFactory } from '../../factories/server-and-event-streams-factory.js'; describe('ServerAndEventStreamsFactory', () => { describe('create', () => { @@ -8,11 +7,13 @@ describe('ServerAndEventStreamsFactory', () => { const server = { test: 'test' }; const result = ServerAndEventStreamsFactory.create(server); - expect(result).to.have.keys('init', 'connection', 'disconnect', 'server'); - expect(result.init instanceof ReplaySubject).to.be.true; - expect(result.connection instanceof Subject).to.be.true; - expect(result.disconnect instanceof Subject).to.be.true; - expect(result.server).to.be.eql(server); + expect(Object.keys(result)).toEqual( + expect.arrayContaining(['init', 'connection', 'disconnect', 'server']), + ); + expect(result.init instanceof ReplaySubject).toBe(true); + expect(result.connection instanceof Subject).toBe(true); + expect(result.disconnect instanceof Subject).toBe(true); + expect(result.server).toEqual(server); }); }); }); diff --git a/packages/websockets/test/factories/ws-params-factory.spec.ts b/packages/websockets/test/factories/ws-params-factory.spec.ts index a0e95883bf5..005483e857c 100644 --- a/packages/websockets/test/factories/ws-params-factory.spec.ts +++ b/packages/websockets/test/factories/ws-params-factory.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { WsParamtype } from '../../enums/ws-paramtype.enum'; -import { WsParamsFactory } from '../../factories/ws-params-factory'; +import { WsParamtype } from '../../enums/ws-paramtype.enum.js'; +import { WsParamsFactory } from '../../factories/ws-params-factory.js'; describe('WsParamsFactory', () => { let factory: WsParamsFactory; @@ -17,32 +16,30 @@ describe('WsParamsFactory', () => { it('should return a message payload object', () => { expect( factory.exchangeKeyForValue(WsParamtype.PAYLOAD, null!, args), - ).to.be.eql(data); + ).toEqual(data); }); it('should return a message payload object with parameter extraction', () => { expect( factory.exchangeKeyForValue(WsParamtype.PAYLOAD, 'data', args), - ).to.be.eql(data.data); + ).toEqual(data.data); }); }); describe(`WsParamtype.SOCKET`, () => { it('should return a connected socket object', () => { expect( factory.exchangeKeyForValue(WsParamtype.SOCKET, null!, args), - ).to.be.eql(client); + ).toEqual(client); }); }); }); describe('when key is not available', () => { it('should return null', () => { - expect(factory.exchangeKeyForValue(-1, null!, [])).to.be.eql(null); + expect(factory.exchangeKeyForValue(-1, null!, [])).toEqual(null); }); }); describe('when args are not available', () => { it('should return null', () => { - expect(factory.exchangeKeyForValue(null!, null!, null!)).to.be.eql( - null, - ); + expect(factory.exchangeKeyForValue(null!, null!, null!)).toEqual(null); }); }); }); diff --git a/packages/websockets/test/gateway-metadata-explorer.spec.ts b/packages/websockets/test/gateway-metadata-explorer.spec.ts index f5d4488a901..32e660a8711 100644 --- a/packages/websockets/test/gateway-metadata-explorer.spec.ts +++ b/packages/websockets/test/gateway-metadata-explorer.spec.ts @@ -1,11 +1,9 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { MetadataScanner } from '../../core/metadata-scanner'; -import { WebSocketServer } from '../decorators/gateway-server.decorator'; -import { WebSocketGateway } from '../decorators/socket-gateway.decorator'; -import { SubscribeMessage } from '../decorators/subscribe-message.decorator'; -import { Ack } from '../decorators/ack.decorator'; -import { GatewayMetadataExplorer } from '../gateway-metadata-explorer'; +import { MetadataScanner } from '../../core/metadata-scanner.js'; +import { Ack } from '../decorators/ack.decorator.js'; +import { WebSocketServer } from '../decorators/gateway-server.decorator.js'; +import { WebSocketGateway } from '../decorators/socket-gateway.decorator.js'; +import { SubscribeMessage } from '../decorators/subscribe-message.decorator.js'; +import { GatewayMetadataExplorer } from '../gateway-metadata-explorer.js'; describe('GatewayMetadataExplorer', () => { const message = 'test'; @@ -43,16 +41,16 @@ describe('GatewayMetadataExplorer', () => { instance = new GatewayMetadataExplorer(scanner); }); describe('explore', () => { - let getAllMethodNames: sinon.SinonSpy; + let getAllMethodNames: ReturnType; beforeEach(() => { - getAllMethodNames = sinon.spy(scanner, 'getAllMethodNames'); + getAllMethodNames = vi.spyOn(scanner, 'getAllMethodNames'); }); it(`should call "scanFromPrototype" with expected arguments`, () => { const obj = new Test(); instance.explore(obj as any); - const [argProto] = getAllMethodNames.getCall(0).args; - expect(argProto).to.be.eql(Object.getPrototypeOf(obj)); + const [argProto] = getAllMethodNames.mock.calls[0]; + expect(argProto).toEqual(Object.getPrototypeOf(obj)); }); }); describe('exploreMethodMetadata', () => { @@ -62,25 +60,27 @@ describe('GatewayMetadataExplorer', () => { }); it(`should return null when "isMessageMapping" metadata is undefined`, () => { const metadata = instance.exploreMethodMetadata(test, 'noMessage'); - expect(metadata).to.eq(null); + expect(metadata).toBeNull(); }); it(`should return message mapping properties when "isMessageMapping" metadata is not undefined`, () => { const metadata = instance.exploreMethodMetadata(test, 'test')!; - expect(metadata).to.have.keys([ - 'callback', - 'message', - 'methodName', - 'isAckHandledManually', - ]); - expect(metadata.message).to.eql(message); + expect(Object.keys(metadata)).toEqual( + expect.arrayContaining([ + 'callback', + 'message', + 'methodName', + 'isAckHandledManually', + ]), + ); + expect(metadata.message).toEqual(message); }); it('should set "isAckHandledManually" property to true when @Ack decorator is used', () => { const metadata = instance.exploreMethodMetadata(test, 'testWithAck')!; - expect(metadata.isAckHandledManually).to.be.true; + expect(metadata.isAckHandledManually).toBe(true); }); it('should set "isAckHandledManually" property to false when @Ack decorator is not used', () => { const metadata = instance.exploreMethodMetadata(test, 'test')!; - expect(metadata.isAckHandledManually).to.be.false; + expect(metadata.isAckHandledManually).toBe(false); }); }); describe('scanForServerHooks', () => { @@ -88,8 +88,8 @@ describe('GatewayMetadataExplorer', () => { const obj = new Test(); const servers = [...instance.scanForServerHooks(obj as any)]; - expect(servers).to.have.length(2); - expect(servers).to.deep.eq(['server', 'anotherServer']); + expect(servers).toHaveLength(2); + expect(servers).toEqual(['server', 'anotherServer']); }); }); }); diff --git a/packages/websockets/test/socket-server-provider.spec.ts b/packages/websockets/test/socket-server-provider.spec.ts index a9d1649db63..037fc1bc1be 100644 --- a/packages/websockets/test/socket-server-provider.spec.ts +++ b/packages/websockets/test/socket-server-provider.spec.ts @@ -1,9 +1,7 @@ -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { AbstractWsAdapter } from '../adapters/ws-adapter'; -import { SocketServerProvider } from '../socket-server-provider'; -import { SocketsContainer } from '../sockets-container'; +import { ApplicationConfig } from '@nestjs/core/application-config.js'; +import { AbstractWsAdapter } from '../adapters/ws-adapter.js'; +import { SocketServerProvider } from '../socket-server-provider.js'; +import { SocketsContainer } from '../sockets-container.js'; class NoopAdapter extends AbstractWsAdapter { public create(port: number, options?: any) {} @@ -12,68 +10,68 @@ class NoopAdapter extends AbstractWsAdapter { describe('SocketServerProvider', () => { let instance: SocketServerProvider; - let socketsContainer: SocketsContainer, mockContainer: sinon.SinonMock; + let socketsContainer: SocketsContainer; beforeEach(() => { socketsContainer = new SocketsContainer(); - mockContainer = sinon.mock(socketsContainer); instance = new SocketServerProvider( socketsContainer, new ApplicationConfig(new NoopAdapter()), ); }); describe('scanForSocketServer', () => { - let createSocketServerSpy: sinon.SinonSpy; + let createSocketServerSpy: ReturnType; const path = 'localhost:3030'; const port = 30; beforeEach(() => { - createSocketServerSpy = sinon.spy(instance, 'createSocketServer' as any); + createSocketServerSpy = vi.spyOn(instance, 'createSocketServer' as any); }); - afterEach(() => { - mockContainer.restore(); - }); it(`should return stored server`, () => { const server = { test: 'test' }; - mockContainer.expects('getOneByConfig').returns(server); + vi.spyOn(socketsContainer, 'getOneByConfig').mockReturnValue( + server as any, + ); const result = instance.scanForSocketServer({ namespace: null! }, port); - expect(createSocketServerSpy.called).to.be.false; - expect(result).to.eq(server); + expect(createSocketServerSpy).not.toHaveBeenCalled(); + expect(result).toBe(server); }); it(`should call "createSocketServer" when server is not stored already`, () => { - mockContainer.expects('getOneByConfig').returns(null); + vi.spyOn(socketsContainer, 'getOneByConfig').mockReturnValue(null as any); instance.scanForSocketServer({ path }, port); - expect(createSocketServerSpy.called).to.be.true; + expect(createSocketServerSpy).toHaveBeenCalled(); }); it(`should call "decorateWithNamespace" when namespace is specified`, () => { - const decorateWithNamespaceSpy = sinon.spy( + const decorateWithNamespaceSpy = vi.spyOn( instance, 'decorateWithNamespace' as any, ); instance.scanForSocketServer({ path, namespace: 'random' }, port); - expect(decorateWithNamespaceSpy.called).to.be.true; + expect(decorateWithNamespaceSpy).toHaveBeenCalled(); }); describe('when namespace is specified and server does exist already', () => { it(`should call "decorateWithNamespace" and not call "createSocketServer"`, () => { const server = { test: 'test' }; - mockContainer.expects('getOneByConfig').returns(server); + vi.spyOn(socketsContainer, 'getOneByConfig').mockReturnValue( + server as any, + ); - const decorateWithNamespaceSpy = sinon.spy( + const decorateWithNamespaceSpy = vi.spyOn( instance, 'decorateWithNamespace' as any, ); instance.scanForSocketServer({ path, namespace: 'random' }, port); - expect(decorateWithNamespaceSpy.called).to.be.true; - expect(createSocketServerSpy.called).to.be.false; + expect(decorateWithNamespaceSpy).toHaveBeenCalled(); + expect(createSocketServerSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/websockets/test/utils/compare-element.util.spec.ts b/packages/websockets/test/utils/compare-element.util.spec.ts index 74e4b6c4093..9461e4c3179 100644 --- a/packages/websockets/test/utils/compare-element.util.spec.ts +++ b/packages/websockets/test/utils/compare-element.util.spec.ts @@ -1,9 +1,8 @@ -import { expect } from 'chai'; -import { compareElementAt } from '../../utils/compare-element.util'; +import { compareElementAt } from '../../utils/compare-element.util.js'; describe('compareElementAt', () => { it('should compare elements at the specific position in arrays', () => { - expect(compareElementAt([0, 1, 0], [2, 1, 7], 1)).to.be.true; - expect(compareElementAt([0, 1, 0], [2, 0, 7], 1)).to.be.false; + expect(compareElementAt([0, 1, 0], [2, 1, 7], 1)).toBe(true); + expect(compareElementAt([0, 1, 0], [2, 0, 7], 1)).toBe(false); }); }); diff --git a/packages/websockets/test/utils/gateway-server.decorator.spec.ts b/packages/websockets/test/utils/gateway-server.decorator.spec.ts index 1150da09658..901da6567e3 100644 --- a/packages/websockets/test/utils/gateway-server.decorator.spec.ts +++ b/packages/websockets/test/utils/gateway-server.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { WebSocketServer } from '../../decorators/gateway-server.decorator'; -import { GATEWAY_SERVER_METADATA } from '../../constants'; +import { WebSocketServer } from '../../decorators/gateway-server.decorator.js'; +import { GATEWAY_SERVER_METADATA } from '../../constants.js'; describe('@WebSocketServer', () => { class TestGateway { @@ -13,9 +12,9 @@ describe('@WebSocketServer', () => { TestGateway, 'server', ); - expect(isServer).to.be.eql(true); + expect(isServer).toEqual(true); }); it('should set property value to null by default', () => { - expect(TestGateway.server).to.be.eql(null); + expect(TestGateway.server).toEqual(null); }); }); diff --git a/packages/websockets/test/utils/socket-gateway.decorator.spec.ts b/packages/websockets/test/utils/socket-gateway.decorator.spec.ts index 81d94b361f7..bf70074f920 100644 --- a/packages/websockets/test/utils/socket-gateway.decorator.spec.ts +++ b/packages/websockets/test/utils/socket-gateway.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { GATEWAY_METADATA, GATEWAY_OPTIONS } from '../../constants'; -import { WebSocketGateway } from '../../decorators/socket-gateway.decorator'; +import { GATEWAY_METADATA, GATEWAY_OPTIONS } from '../../constants.js'; +import { WebSocketGateway } from '../../decorators/socket-gateway.decorator.js'; describe('@WebSocketGateway', () => { @WebSocketGateway(80, { namespace: '/' }) @@ -11,9 +10,9 @@ describe('@WebSocketGateway', () => { const port = Reflect.getMetadata('port', TestGateway); const { namespace } = Reflect.getMetadata(GATEWAY_OPTIONS, TestGateway); - expect(isGateway).to.be.eql(true); - expect(port).to.be.eql(80); - expect(namespace).to.be.eql('/'); + expect(isGateway).toEqual(true); + expect(port).toEqual(80); + expect(namespace).toEqual('/'); }); @WebSocketGateway() @@ -23,8 +22,8 @@ describe('@WebSocketGateway', () => { const isGateway = Reflect.getMetadata(GATEWAY_METADATA, TestGateway2); const port = Reflect.getMetadata('port', TestGateway2); - expect(isGateway).to.be.eql(true); - expect(port).to.be.eql(0); + expect(isGateway).toEqual(true); + expect(port).toEqual(0); }); @WebSocketGateway({ namespace: '/' }) @@ -35,8 +34,8 @@ describe('@WebSocketGateway', () => { const port = Reflect.getMetadata('port', TestGateway3); const { namespace } = Reflect.getMetadata(GATEWAY_OPTIONS, TestGateway3); - expect(isGateway).to.be.eql(true); - expect(port).to.be.eql(0); - expect(namespace).to.be.eql('/'); + expect(isGateway).toEqual(true); + expect(port).toEqual(0); + expect(namespace).toEqual('/'); }); }); diff --git a/packages/websockets/test/utils/subscribe-message.decorator.spec.ts b/packages/websockets/test/utils/subscribe-message.decorator.spec.ts index 3479e7bdb3f..77592361a05 100644 --- a/packages/websockets/test/utils/subscribe-message.decorator.spec.ts +++ b/packages/websockets/test/utils/subscribe-message.decorator.spec.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; -import { MESSAGE_MAPPING_METADATA } from '../../constants'; -import { SubscribeMessage } from '../../decorators/subscribe-message.decorator'; +import { MESSAGE_MAPPING_METADATA } from '../../constants.js'; +import { SubscribeMessage } from '../../decorators/subscribe-message.decorator.js'; describe('@SubscribeMessage', () => { class TestGateway { @@ -15,7 +14,7 @@ describe('@SubscribeMessage', () => { ); const message = Reflect.getMetadata('message', TestGateway.fn); - expect(isMessageMapping).to.be.true; - expect(message).to.be.eql('filter'); + expect(isMessageMapping).toBe(true); + expect(message).toEqual('filter'); }); }); diff --git a/packages/websockets/test/web-sockets-controller.spec.ts b/packages/websockets/test/web-sockets-controller.spec.ts index ffa77f90092..da5b643a2c4 100644 --- a/packages/websockets/test/web-sockets-controller.spec.ts +++ b/packages/websockets/test/web-sockets-controller.spec.ts @@ -1,21 +1,19 @@ import { NestContainer } from '@nestjs/core'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { expect } from 'chai'; +import { ApplicationConfig } from '@nestjs/core/application-config.js'; import { fromEvent, lastValueFrom, Observable, of } from 'rxjs'; -import * as sinon from 'sinon'; -import { GraphInspector } from '../../core/inspector/graph-inspector'; -import { MetadataScanner } from '../../core/metadata-scanner'; -import { AbstractWsAdapter } from '../adapters/ws-adapter'; -import { PORT_METADATA } from '../constants'; -import { WsContextCreator } from '../context/ws-context-creator'; -import { WebSocketGateway } from '../decorators/socket-gateway.decorator'; -import { InvalidSocketPortException } from '../errors/invalid-socket-port.exception'; +import { GraphInspector } from '../../core/inspector/graph-inspector.js'; +import { MetadataScanner } from '../../core/metadata-scanner.js'; +import { AbstractWsAdapter } from '../adapters/ws-adapter.js'; +import { PORT_METADATA } from '../constants.js'; +import { WsContextCreator } from '../context/ws-context-creator.js'; +import { WebSocketGateway } from '../decorators/socket-gateway.decorator.js'; +import { InvalidSocketPortException } from '../errors/invalid-socket-port.exception.js'; import { GatewayMetadataExplorer, MessageMappingProperties, -} from '../gateway-metadata-explorer'; -import { SocketServerProvider } from '../socket-server-provider'; -import { WebSocketsController } from '../web-sockets-controller'; +} from '../gateway-metadata-explorer.js'; +import { SocketServerProvider } from '../socket-server-provider.js'; +import { WebSocketsController } from '../web-sockets-controller.js'; class NoopAdapter extends AbstractWsAdapter { public create(port: number, options?: any) {} @@ -36,8 +34,7 @@ describe('WebSocketsController', () => { let untypedInstance: any; let provider: SocketServerProvider, graphInspector: GraphInspector, - config: ApplicationConfig, - mockProvider: sinon.SinonMock; + config: ApplicationConfig; const messageHandlerCallback = () => Promise.resolve(); const port = 90, @@ -49,20 +46,26 @@ describe('WebSocketsController', () => { config = new ApplicationConfig(new NoopAdapter()); provider = new SocketServerProvider(null!, config); graphInspector = new GraphInspector(new NestContainer()); - mockProvider = sinon.mock(provider); - const contextCreator = sinon.createStubInstance(WsContextCreator); - contextCreator.create.returns(messageHandlerCallback); + const contextCreator = { + ...Object.fromEntries( + Object.getOwnPropertyNames(WsContextCreator.prototype).map(m => [ + m, + vi.fn(), + ]), + ), + } as any; + contextCreator.create.mockReturnValue(messageHandlerCallback); instance = new WebSocketsController( provider, config, - contextCreator as any, + contextCreator, graphInspector, ); untypedInstance = instance as any; }); describe('connectGatewayToServer', () => { - let subscribeToServerEvents: sinon.SinonSpy; + let subscribeToServerEvents: ReturnType; @WebSocketGateway('test' as any) class InvalidGateway {} @@ -71,7 +74,7 @@ describe('WebSocketsController', () => { class DefaultGateway {} beforeEach(() => { - subscribeToServerEvents = sinon.spy(); + subscribeToServerEvents = vi.fn(); untypedInstance.subscribeToServerEvents = subscribeToServerEvents; }); it('should throw "InvalidSocketPortException" when port is not a number', () => { @@ -83,7 +86,7 @@ describe('WebSocketsController', () => { 'moduleKey', 'instanceWrapperId', ), - ).throws(InvalidSocketPortException); + ).toThrow(InvalidSocketPortException); }); it('should call "subscribeToServerEvents" with default values when metadata is empty', () => { const gateway = new DefaultGateway(); @@ -93,8 +96,13 @@ describe('WebSocketsController', () => { 'moduleKey', 'instanceWrapperId', ); - expect(subscribeToServerEvents.calledWith(gateway, {}, 0, 'moduleKey')).to - .be.true; + expect(subscribeToServerEvents).toHaveBeenCalledWith( + gateway, + {}, + 0, + 'moduleKey', + 'instanceWrapperId', + ); }); it('should call "subscribeToServerEvents" when metadata is valid', () => { const gateway = new Test(); @@ -104,30 +112,27 @@ describe('WebSocketsController', () => { 'moduleKey', 'instanceWrapperId', ); - expect( - subscribeToServerEvents.calledWith( - gateway, - { namespace }, - port, - 'moduleKey', - ), - ).to.be.true; + expect(subscribeToServerEvents).toHaveBeenCalledWith( + gateway, + { namespace }, + port, + 'moduleKey', + 'instanceWrapperId', + ); }); }); describe('subscribeToServerEvents', () => { let explorer: GatewayMetadataExplorer, - mockExplorer: sinon.SinonMock, gateway, handlers, server, - assignServerToProperties: sinon.SinonSpy, - subscribeEvents: sinon.SinonSpy; + assignServerToProperties: ReturnType, + subscribeEvents: ReturnType; const handlerCallback = () => {}; beforeEach(() => { gateway = new Test(); explorer = new GatewayMetadataExplorer(new MetadataScanner()); - mockExplorer = sinon.mock(explorer); untypedInstance.metadataExplorer = explorer; handlers = [ @@ -140,11 +145,11 @@ describe('WebSocketsController', () => { ]; server = { server: 'test' }; - mockExplorer.expects('explore').returns(handlers); - mockProvider.expects('scanForSocketServer').returns(server); + vi.spyOn(explorer, 'explore').mockReturnValue(handlers); + vi.spyOn(provider, 'scanForSocketServer').mockReturnValue(server); - assignServerToProperties = sinon.spy(); - subscribeEvents = sinon.spy(); + assignServerToProperties = vi.fn(); + subscribeEvents = vi.fn(); instance['assignServerToProperties'] = assignServerToProperties; instance['subscribeEvents'] = subscribeEvents; }); @@ -156,8 +161,10 @@ describe('WebSocketsController', () => { 'moduleKey', 'instanceWrapperId', ); - expect(assignServerToProperties.calledWith(gateway, server.server)).to.be - .true; + expect(assignServerToProperties).toHaveBeenCalledWith( + gateway, + server.server, + ); }); it('should call "subscribeEvents" with expected arguments', () => { instance.subscribeToServerEvents( @@ -167,9 +174,9 @@ describe('WebSocketsController', () => { 'moduleKey', 'instanceWrapperId', ); - expect(subscribeEvents.firstCall.args[0]).to.be.equal(gateway); - expect(subscribeEvents.firstCall.args[2]).to.be.equal(server); - expect(subscribeEvents.firstCall.args[1]).to.be.eql([ + expect(subscribeEvents.mock.calls[0][0]).toBe(gateway); + expect(subscribeEvents.mock.calls[0][2]).toBe(server); + expect(subscribeEvents.mock.calls[0][1]).toEqual([ { message: 'message', methodName: 'methodName', @@ -199,7 +206,7 @@ describe('WebSocketsController', () => { isAckHandledManually: false, }, ]; - const insertEntrypointDefinitionSpy = sinon.spy( + const insertEntrypointDefinitionSpy = vi.spyOn( graphInspector, 'insertEntrypointDefinition', ); @@ -210,9 +217,9 @@ describe('WebSocketsController', () => { instanceWrapperId, ); - expect(insertEntrypointDefinitionSpy.calledTwice).to.be.true; - expect( - insertEntrypointDefinitionSpy.calledWith({ + expect(insertEntrypointDefinitionSpy).toHaveBeenCalledTimes(2); + expect(insertEntrypointDefinitionSpy).toHaveBeenCalledWith( + { type: 'websocket', methodName: messageHandlers[0].methodName, className: GatewayHostCls.name, @@ -222,10 +229,11 @@ describe('WebSocketsController', () => { key: messageHandlers[0].message, message: messageHandlers[0].message, } as any, - }), - ).to.be.true; - expect( - insertEntrypointDefinitionSpy.calledWith({ + }, + instanceWrapperId, + ); + expect(insertEntrypointDefinitionSpy).toHaveBeenCalledWith( + { type: 'websocket', methodName: messageHandlers[1].methodName, className: GatewayHostCls.name, @@ -235,8 +243,9 @@ describe('WebSocketsController', () => { key: messageHandlers[1].message, message: messageHandlers[1].message, } as any, - }), - ).to.be.true; + }, + instanceWrapperId, + ); }); }); describe('subscribeEvents', () => { @@ -244,20 +253,20 @@ describe('WebSocketsController', () => { let handlers: any; let server: any, - subscribeConnectionEvent: sinon.SinonSpy, - subscribeDisconnectEvent: sinon.SinonSpy, - nextSpy: sinon.SinonSpy, - onSpy: sinon.SinonSpy, - subscribeInitEvent: sinon.SinonSpy, - getConnectionHandler: sinon.SinonSpy; + subscribeConnectionEvent: ReturnType, + subscribeDisconnectEvent: ReturnType, + nextSpy: ReturnType, + onSpy: ReturnType, + subscribeInitEvent: ReturnType, + getConnectionHandler: ReturnType; beforeEach(() => { - nextSpy = sinon.spy(); - onSpy = sinon.spy(); - subscribeInitEvent = sinon.spy(); - getConnectionHandler = sinon.spy(); - subscribeConnectionEvent = sinon.spy(); - subscribeDisconnectEvent = sinon.spy(); + nextSpy = vi.fn(); + onSpy = vi.fn(); + subscribeInitEvent = vi.fn(); + getConnectionHandler = vi.fn(); + subscribeConnectionEvent = vi.fn(); + subscribeDisconnectEvent = vi.fn(); handlers = ['test']; server = { @@ -278,33 +287,35 @@ describe('WebSocketsController', () => { it('should call "subscribeConnectionEvent" with expected arguments', () => { instance.subscribeEvents(gateway, handlers, server); - expect(subscribeConnectionEvent.calledWith(gateway, server.connection)).to - .be.true; + expect(subscribeConnectionEvent).toHaveBeenCalledWith( + gateway, + server.connection, + ); }); it('should call "subscribeDisconnectEvent" with expected arguments', () => { instance.subscribeEvents(gateway, handlers, server); - expect(subscribeDisconnectEvent.calledWith(gateway, server.disconnect)).to - .be.true; + expect(subscribeDisconnectEvent).toHaveBeenCalledWith( + gateway, + server.disconnect, + ); }); it('should call "subscribeInitEvent" with expected arguments', () => { instance.subscribeEvents(gateway, handlers, server); - expect(subscribeInitEvent.calledWith(gateway, server.init)).to.be.true; + expect(subscribeInitEvent).toHaveBeenCalledWith(gateway, server.init); }); it('should bind connection handler to server', () => { instance.subscribeEvents(gateway, handlers, server); - expect(onSpy.calledWith('connection', getConnectionHandler())).to.be.true; + expect(onSpy).toHaveBeenCalledWith('connection', getConnectionHandler()); }); it('should call "getConnectionHandler" with expected arguments', () => { instance.subscribeEvents(gateway, handlers, server); - expect( - getConnectionHandler.calledWith( - instance, - gateway, - handlers, - server.disconnect, - server.connection, - ), - ).to.be.true; + expect(getConnectionHandler).toHaveBeenCalledWith( + instance, + gateway, + handlers, + server.disconnect, + server.connection, + ); }); }); describe('getConnectionHandler', () => { @@ -313,18 +324,18 @@ describe('WebSocketsController', () => { let handlers, fn; let connection, client, - nextSpy: sinon.SinonSpy, - onSpy: sinon.SinonSpy, - subscribeMessages: sinon.SinonSpy, - subscribeDisconnectEvent: sinon.SinonSpy, - subscribeConnectionEvent: sinon.SinonSpy; + nextSpy: ReturnType, + onSpy: ReturnType, + subscribeMessages: ReturnType, + subscribeDisconnectEvent: ReturnType, + subscribeConnectionEvent: ReturnType; beforeEach(() => { - nextSpy = sinon.spy(); - onSpy = sinon.spy(); - subscribeMessages = sinon.spy(); - subscribeDisconnectEvent = sinon.spy(); - subscribeConnectionEvent = sinon.spy(); + nextSpy = vi.fn(); + onSpy = vi.fn(); + subscribeMessages = vi.fn(); + subscribeDisconnectEvent = vi.fn(); + subscribeConnectionEvent = vi.fn(); handlers = ['test']; connection = { @@ -350,80 +361,145 @@ describe('WebSocketsController', () => { it('should return function', () => { expect( instance.getConnectionHandler(null!, null!, null!, null!, null!), - ).to.be.a('function'); + ).toBeTypeOf('function'); }); it('should call "next" method of connection object with expected argument', () => { - expect(nextSpy.calledWith([client])).to.be.true; + expect(nextSpy).toHaveBeenCalledWith([client]); }); it('should call "subscribeMessages" with expected arguments', () => { - expect(subscribeMessages.calledWith(handlers, client, gateway)).to.be - .true; + expect(subscribeMessages).toHaveBeenCalledWith(handlers, client, gateway); }); it('should call "on" method of client object with expected arguments', () => { - expect(onSpy.called).to.be.true; + expect(onSpy).toHaveBeenCalled(); }); }); describe('subscribeInitEvent', () => { const gateway = new Test(); - let event: any, subscribe: sinon.SinonSpy; + let event: any, subscribe: ReturnType; beforeEach(() => { - subscribe = sinon.spy(); - event = { subscribe, pipe: sinon.stub().returnsThis() }; + subscribe = vi.fn(); + event = { subscribe, pipe: vi.fn().mockReturnThis() }; }); it('should not call subscribe method when "afterInit" method not exists', () => { instance.subscribeInitEvent(gateway, event); - expect(subscribe.called).to.be.false; + expect(subscribe).not.toHaveBeenCalled(); }); it('should call subscribe method of event object with expected arguments when "afterInit" exists', () => { (gateway as any).afterInit = () => {}; instance.subscribeInitEvent(gateway, event); - expect(subscribe.called).to.be.true; + expect(subscribe).toHaveBeenCalled(); }); }); describe('subscribeConnectionEvent', () => { const gateway = new Test(); - let event, subscribe: sinon.SinonSpy; + let event, subscribe: ReturnType; beforeEach(() => { - subscribe = sinon.spy(); - event = { subscribe, pipe: sinon.stub().returnsThis() }; + subscribe = vi.fn(); + event = { subscribe, pipe: vi.fn().mockReturnThis() }; }); it('should not call subscribe method when "handleConnection" method not exists', () => { instance.subscribeConnectionEvent(gateway, event); - expect(subscribe.called).to.be.false; + expect(subscribe).not.toHaveBeenCalled(); }); it('should call subscribe method of event object with expected arguments when "handleConnection" exists', () => { (gateway as any).handleConnection = () => {}; instance.subscribeConnectionEvent(gateway, event); - expect(subscribe.called).to.be.true; + expect(subscribe).toHaveBeenCalled(); }); }); describe('subscribeDisconnectEvent', () => { const gateway = new Test(); - let event, subscribe: sinon.SinonSpy; + let event, subscribe: ReturnType; beforeEach(() => { - subscribe = sinon.spy(); - event = { subscribe, pipe: sinon.stub().returnsThis() }; + subscribe = vi.fn(); + event = { subscribe, pipe: vi.fn().mockReturnThis() }; }); it('should not call subscribe method when "handleDisconnect" method not exists', () => { instance.subscribeDisconnectEvent(gateway, event); - expect(subscribe.called).to.be.false; + expect(subscribe).not.toHaveBeenCalled(); }); it('should call subscribe method of event object with expected arguments when "handleDisconnect" exists', () => { (gateway as any).handleDisconnect = () => {}; instance.subscribeDisconnectEvent(gateway, event); - expect(subscribe.called).to.be.true; + expect(subscribe).toHaveBeenCalled(); + }); + + describe('when handling disconnect events', () => { + let handleDisconnectSpy: ReturnType; + + beforeEach(() => { + handleDisconnectSpy = vi.fn(); + (gateway as any).handleDisconnect = handleDisconnectSpy; + }); + + it('should call handleDisconnect with client and reason when data contains both', () => { + const mockClient = { id: 'test-client' }; + const mockReason = 'client namespace disconnect'; + const disconnectData = { client: mockClient, reason: mockReason }; + + let subscriptionCallback: Function | undefined; + event.subscribe = (callback: Function) => { + subscriptionCallback = callback; + }; + + instance.subscribeDisconnectEvent(gateway, event); + + if (subscriptionCallback) { + subscriptionCallback(disconnectData); + } + + expect(handleDisconnectSpy).toHaveBeenCalledOnce(); + expect(handleDisconnectSpy).toHaveBeenCalledWith( + mockClient, + mockReason, + ); + }); + + it('should call handleDisconnect with only client for backward compatibility', () => { + const mockClient = { id: 'test-client' }; + + let subscriptionCallback: Function | undefined; + event.subscribe = (callback: Function) => { + subscriptionCallback = callback; + }; + + instance.subscribeDisconnectEvent(gateway, event); + + if (subscriptionCallback) { + subscriptionCallback(mockClient); + } + + expect(handleDisconnectSpy).toHaveBeenCalledOnce(); + expect(handleDisconnectSpy).toHaveBeenCalledWith(mockClient); + }); + + it('should handle null/undefined data gracefully', () => { + let subscriptionCallback: Function | undefined; + event.subscribe = (callback: Function) => { + subscriptionCallback = callback; + }; + + instance.subscribeDisconnectEvent(gateway, event); + + if (subscriptionCallback) { + subscriptionCallback(null); + } + + expect(handleDisconnectSpy).toHaveBeenCalledOnce(); + expect(handleDisconnectSpy).toHaveBeenCalledWith(null); + }); }); }); describe('subscribeMessages', () => { const gateway = new Test(); - let client, handlers, onSpy: sinon.SinonSpy; + let client, handlers, onSpy: ReturnType; beforeEach(() => { - onSpy = sinon.spy(); + onSpy = vi.fn(); client = { on: onSpy, off: onSpy }; handlers = [ @@ -441,23 +517,23 @@ describe('WebSocketsController', () => { }); it('should bind each handler to client', () => { instance.subscribeMessages(handlers, client, gateway); - expect(onSpy.calledTwice).to.be.true; + expect(onSpy).toHaveBeenCalledTimes(2); }); it('should pass "isAckHandledManually" flag to the adapter', () => { const adapter = config.getIoAdapter(); - const bindMessageHandlersSpy = sinon.spy(adapter, 'bindMessageHandlers'); + const bindMessageHandlersSpy = vi.spyOn(adapter, 'bindMessageHandlers'); instance.subscribeMessages(handlers, client, gateway); - const handlersPassedToAdapter = bindMessageHandlersSpy.firstCall.args[1]; + const handlersPassedToAdapter = bindMessageHandlersSpy.mock.calls[0][1]; - expect(handlersPassedToAdapter[0].message).to.equal(handlers[0].message); - expect(handlersPassedToAdapter[0].isAckHandledManually).to.equal( + expect(handlersPassedToAdapter[0].message).toBe(handlers[0].message); + expect(handlersPassedToAdapter[0].isAckHandledManually).toBe( handlers[0].isAckHandledManually, ); - expect(handlersPassedToAdapter[1].message).to.equal(handlers[1].message); - expect(handlersPassedToAdapter[1].isAckHandledManually).to.equal( + expect(handlersPassedToAdapter[1].message).toBe(handlers[1].message); + expect(handlersPassedToAdapter[1].isAckHandledManually).toBe( handlers[1].isAckHandledManually, ); }); @@ -473,7 +549,7 @@ describe('WebSocketsController', () => { Promise.resolve(Promise.resolve(value)), ), ), - ).to.be.eq(value); + ).toBe(value); }); }); @@ -484,7 +560,7 @@ describe('WebSocketsController', () => { await lastValueFrom( await instance.pickResult(Promise.resolve(of(value))), ), - ).to.be.eq(value); + ).toBe(value); }); }); @@ -495,7 +571,7 @@ describe('WebSocketsController', () => { await lastValueFrom( await instance.pickResult(Promise.resolve(value)), ), - ).to.equal(value); + ).toBe(value); }); }); @@ -506,9 +582,109 @@ describe('WebSocketsController', () => { await lastValueFrom( await instance.pickResult(Promise.resolve(value)), ), - ).to.be.eq(value); + ).toBe(value); }); }); }); }); + + describe('connectGatewayToServer', () => { + it('should use port 0 when PORT_METADATA is not defined', () => { + const subscribeToServerEvents = vi.fn(); + untypedInstance.subscribeToServerEvents = subscribeToServerEvents; + + @WebSocketGateway() + class EmptyGateway {} + const gateway = new EmptyGateway(); + + instance.connectGatewayToServer(gateway, EmptyGateway, 'mod', 'wrId'); + expect(subscribeToServerEvents).toHaveBeenCalledWith( + gateway, + {}, + 0, + 'mod', + 'wrId', + ); + }); + }); + + describe('subscribeToServerEvents', () => { + it('should return early when appOptions.preview is true', () => { + const previewConfig = new ApplicationConfig(new NoopAdapter()); + const previewProvider = new SocketServerProvider(null!, previewConfig); + const contextCreator = { + ...Object.fromEntries( + Object.getOwnPropertyNames(WsContextCreator.prototype).map(m => [ + m, + vi.fn(), + ]), + ), + } as any; + contextCreator.create.mockReturnValue(() => Promise.resolve()); + + const previewInstance = new WebSocketsController( + previewProvider, + previewConfig, + contextCreator, + graphInspector, + { preview: true }, + ); + + const scanSpy = vi.spyOn(previewProvider, 'scanForSocketServer'); + const gateway = new Test(); + + previewInstance.subscribeToServerEvents( + gateway, + { namespace: '/' }, + 90, + 'moduleKey', + 'wrapperId', + ); + + expect(scanSpy).not.toHaveBeenCalled(); + }); + }); + + describe('printSubscriptionLogs', () => { + it('should not throw when gateway has no constructor name', () => { + const noNameGateway = Object.create(null); + expect(() => + untypedInstance.printSubscriptionLogs(noNameGateway, []), + ).not.toThrow(); + }); + + it('should log each message handler', () => { + const logSpy = vi.spyOn(untypedInstance.logger, 'log'); + const gateway = new Test(); + const handlers = [ + { + message: 'msg1', + methodName: 'a', + callback: vi.fn(), + isAckHandledManually: false, + }, + { + message: 'msg2', + methodName: 'b', + callback: vi.fn(), + isAckHandledManually: false, + }, + ]; + untypedInstance.printSubscriptionLogs(gateway, handlers); + expect(logSpy).toHaveBeenCalledTimes(2); + }); + }); + + describe('inspectEntrypointDefinitions', () => { + it('should not throw with empty message handlers', () => { + expect(() => + instance.inspectEntrypointDefinitions( + new Test() as any, + 80, + [], + 'wrapperId', + ), + ).not.toThrow(); + }); + }); }); diff --git a/packages/websockets/tsconfig.build.json b/packages/websockets/tsconfig.build.json index 732ca00cb71..fb0f617fdbe 100644 --- a/packages/websockets/tsconfig.build.json +++ b/packages/websockets/tsconfig.build.json @@ -2,13 +2,7 @@ "extends": "../tsconfig.build.json", "compilerOptions": { "outDir": ".", - "rootDir": ".", - "paths": { - "@nestjs/common": ["../common"], - "@nestjs/common/*": ["../common/*"], - "@nestjs/core": ["../core"], - "@nestjs/core/*": ["../core/*"] - } + "rootDir": "." }, "exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"], "references": [ diff --git a/packages/websockets/utils/index.ts b/packages/websockets/utils/index.ts index d7f90adea9a..20f429261b7 100644 --- a/packages/websockets/utils/index.ts +++ b/packages/websockets/utils/index.ts @@ -1 +1 @@ -export * from './param.utils'; +export * from './param.utils.js'; diff --git a/packages/websockets/utils/param.utils.ts b/packages/websockets/utils/param.utils.ts index 6f539903b6b..23247070e8d 100644 --- a/packages/websockets/utils/param.utils.ts +++ b/packages/websockets/utils/param.utils.ts @@ -1,9 +1,13 @@ -import { PipeTransform, Type } from '@nestjs/common'; -import { assignMetadata } from '@nestjs/common/decorators/http/route-params.decorator'; -import { isNil, isString } from '@nestjs/common/utils/shared.utils'; +import type { + ParameterDecoratorOptions, + PipeTransform, + Type, +} from '@nestjs/common'; +import { assignMetadata } from '@nestjs/common'; +import { isNil, isString } from '@nestjs/common/internal'; import 'reflect-metadata'; -import { PARAM_ARGS_METADATA } from '../constants'; -import { WsParamtype } from '../enums/ws-paramtype.enum'; +import { PARAM_ARGS_METADATA } from '../constants.js'; +import { WsParamtype } from '../enums/ws-paramtype.enum.js'; export function createWsParamDecorator( paramtype: WsParamtype, @@ -15,7 +19,7 @@ export function createWsParamDecorator( {}; Reflect.defineMetadata( PARAM_ARGS_METADATA, - assignMetadata(args, paramtype, index, undefined, ...pipes), + assignMetadata(args, paramtype, index, { pipes }), target.constructor, key!, ); @@ -26,18 +30,66 @@ export const createPipesWsParamDecorator = (paramtype: WsParamtype) => ( data?: any, + optionsOrPipe?: + | ParameterDecoratorOptions + | Type + | PipeTransform, ...pipes: (Type | PipeTransform)[] ): ParameterDecorator => (target, key, index) => { const args = Reflect.getMetadata(PARAM_ARGS_METADATA, target.constructor, key!) || {}; + + const isDataOptions = + data && + typeof data === 'object' && + !('transform' in data) && + ('schema' in data || 'pipes' in data); + + if (isDataOptions) { + const opts = data as ParameterDecoratorOptions; + Reflect.defineMetadata( + PARAM_ARGS_METADATA, + assignMetadata(args, paramtype, index, { + pipes: opts.pipes ?? [], + schema: opts.schema, + }), + target.constructor, + key!, + ); + return; + } + const hasParamData = isNil(data) || isString(data); const paramData = hasParamData ? data : undefined; - const paramPipes = hasParamData ? pipes : [data, ...pipes]; + + const isOptions = + optionsOrPipe && + typeof optionsOrPipe === 'object' && + ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe); + + let paramPipes: (Type | PipeTransform)[]; + if (isOptions) { + paramPipes = optionsOrPipe.pipes ?? []; + } else if (hasParamData) { + paramPipes = [optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]; + } else { + paramPipes = [data, optionsOrPipe, ...pipes].filter(Boolean) as ( + | Type + | PipeTransform + )[]; + } Reflect.defineMetadata( PARAM_ARGS_METADATA, - assignMetadata(args, paramtype, index, paramData!, ...paramPipes), + assignMetadata(args, paramtype, index, { + data: paramData!, + pipes: paramPipes, + schema: isOptions ? optionsOrPipe.schema : undefined, + }), target.constructor, key!, ); diff --git a/packages/websockets/web-sockets-controller.ts b/packages/websockets/web-sockets-controller.ts index 37e21efefdb..d17448f4973 100644 --- a/packages/websockets/web-sockets-controller.ts +++ b/packages/websockets/web-sockets-controller.ts @@ -1,9 +1,3 @@ -import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; -import { Type } from '@nestjs/common/interfaces/type.interface'; -import { Logger } from '@nestjs/common/services/logger.service'; -import { ApplicationConfig } from '@nestjs/core/application-config'; -import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; -import { MetadataScanner } from '@nestjs/core/metadata-scanner'; import { from as fromPromise, isObservable, @@ -12,19 +6,26 @@ import { Subject, } from 'rxjs'; import { distinctUntilChanged, mergeAll } from 'rxjs/operators'; -import { GATEWAY_OPTIONS, PORT_METADATA } from './constants'; -import { WsContextCreator } from './context/ws-context-creator'; -import { InvalidSocketPortException } from './errors/invalid-socket-port.exception'; +import { GATEWAY_OPTIONS, PORT_METADATA } from './constants.js'; +import { WsContextCreator } from './context/ws-context-creator.js'; +import { InvalidSocketPortException } from './errors/invalid-socket-port.exception.js'; import { GatewayMetadataExplorer, MessageMappingProperties, -} from './gateway-metadata-explorer'; -import { GatewayMetadata } from './interfaces/gateway-metadata.interface'; -import { NestGateway } from './interfaces/nest-gateway.interface'; -import { ServerAndEventStreamsHost } from './interfaces/server-and-event-streams-host.interface'; -import { WebsocketEntrypointMetadata } from './interfaces/websockets-entrypoint-metadata.interface'; -import { SocketServerProvider } from './socket-server-provider'; -import { compareElementAt } from './utils/compare-element.util'; +} from './gateway-metadata-explorer.js'; +import { GatewayMetadata } from './interfaces/gateway-metadata.interface.js'; +import { NestGateway } from './interfaces/nest-gateway.interface.js'; +import { ServerAndEventStreamsHost } from './interfaces/server-and-event-streams-host.interface.js'; +import { WebsocketEntrypointMetadata } from './interfaces/websockets-entrypoint-metadata.interface.js'; +import { SocketServerProvider } from './socket-server-provider.js'; +import { compareElementAt } from './utils/compare-element.util.js'; +import type { NestApplicationContextOptions } from '@nestjs/common/internal'; +import { type Type, Logger } from '@nestjs/common'; +import { + type ApplicationConfig, + type GraphInspector, + MetadataScanner, +} from '@nestjs/core'; export class WebSocketsController { private readonly logger = new Logger(WebSocketsController.name, { @@ -141,7 +142,9 @@ export class WebSocketsController { const disconnectHook = adapter.bindClientDisconnect; disconnectHook && - disconnectHook.call(adapter, client, () => disconnect.next(client)); + disconnectHook.call(adapter, client, (reason?: string) => + disconnect.next({ client, reason }), + ); }; } @@ -164,8 +167,21 @@ export class WebSocketsController { public subscribeDisconnectEvent(instance: NestGateway, event: Subject) { if (instance.handleDisconnect) { event - .pipe(distinctUntilChanged()) - .subscribe(instance.handleDisconnect.bind(instance)); + .pipe( + distinctUntilChanged((prev, curr) => { + const prevClient = prev?.client || prev; + const currClient = curr?.client || curr; + return prevClient === currClient; + }), + ) + .subscribe((data: any) => { + if (data && typeof data === 'object' && 'client' in data) { + instance.handleDisconnect!(data.client, data.reason); + } else { + // Backward compatibility: if it's just the client + instance.handleDisconnect!(data); + } + }); } } diff --git a/sample/01-cats-app/e2e/cats/cats.e2e-spec.ts b/sample/01-cats-app/e2e/cats/cats.e2e-spec.ts index 0baf3b62045..79b91ca2226 100644 --- a/sample/01-cats-app/e2e/cats/cats.e2e-spec.ts +++ b/sample/01-cats-app/e2e/cats/cats.e2e-spec.ts @@ -1,9 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { CatsModule } from '../../src/cats/cats.module'; -import { CatsService } from '../../src/cats/cats.service'; -import { CoreModule } from '../../src/core/core.module'; +import request from 'supertest'; +import { CatsModule } from '../../src/cats/cats.module.js'; +import { CatsService } from '../../src/cats/cats.service.js'; +import { CoreModule } from '../../src/core/core.module.js'; describe('Cats', () => { const catsService = { findAll: () => ['test'] }; diff --git a/sample/01-cats-app/e2e/jest-e2e.json b/sample/01-cats-app/e2e/jest-e2e.json deleted file mode 100644 index 72eb5c3c0a3..00000000000 --- a/sample/01-cats-app/e2e/jest-e2e.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ], - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", - "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], - "coverageReporters": ["json", "lcov"] -} \ No newline at end of file diff --git a/sample/01-cats-app/eslint.config.mjs b/sample/01-cats-app/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/01-cats-app/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/01-cats-app/package.json b/sample/01-cats-app/package.json index b19cbbea1e1..98a64df896a 100644 --- a/sample/01-cats-app/package.json +++ b/sample/01-cats-app/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./e2e/jest-e2e.json" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -29,43 +29,20 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/01-cats-app/src/app.module.ts b/sample/01-cats-app/src/app.module.ts index 0a635f23081..4b6faa16c13 100644 --- a/sample/01-cats-app/src/app.module.ts +++ b/sample/01-cats-app/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsModule } from './cats/cats.module'; -import { CoreModule } from './core/core.module'; +import { CatsModule } from './cats/cats.module.js'; +import { CoreModule } from './core/core.module.js'; @Module({ imports: [CoreModule, CatsModule], diff --git a/sample/01-cats-app/src/cats/cats.controller.spec.ts b/sample/01-cats-app/src/cats/cats.controller.spec.ts index 5eb9ac4f90c..753874846ac 100644 --- a/sample/01-cats-app/src/cats/cats.controller.spec.ts +++ b/sample/01-cats-app/src/cats/cats.controller.spec.ts @@ -1,7 +1,7 @@ import { Test } from '@nestjs/testing'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; -import { Cat } from './interfaces/cat.interface'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; describe('CatsController', () => { let catsController: CatsController; diff --git a/sample/01-cats-app/src/cats/cats.controller.ts b/sample/01-cats-app/src/cats/cats.controller.ts index d9df5203327..2d23d36a724 100644 --- a/sample/01-cats-app/src/cats/cats.controller.ts +++ b/sample/01-cats-app/src/cats/cats.controller.ts @@ -1,10 +1,10 @@ import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common'; -import { Roles } from '../common/decorators/roles.decorator'; -import { RolesGuard } from '../common/guards/roles.guard'; -import { ParseIntPipe } from '../common/pipes/parse-int.pipe'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './interfaces/cat.interface'; +import { Roles } from '../common/decorators/roles.decorator.js'; +import { RolesGuard } from '../common/guards/roles.guard.js'; +import { ParseIntPipe } from '../common/pipes/parse-int.pipe.js'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; @UseGuards(RolesGuard) @Controller('cats') diff --git a/sample/01-cats-app/src/cats/cats.module.ts b/sample/01-cats-app/src/cats/cats.module.ts index f3291c7d11e..14b21cd9f14 100644 --- a/sample/01-cats-app/src/cats/cats.module.ts +++ b/sample/01-cats-app/src/cats/cats.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; @Module({ controllers: [CatsController], diff --git a/sample/01-cats-app/src/cats/cats.service.spec.ts b/sample/01-cats-app/src/cats/cats.service.spec.ts index cdf8ee02ab1..e5ef4d4662f 100644 --- a/sample/01-cats-app/src/cats/cats.service.spec.ts +++ b/sample/01-cats-app/src/cats/cats.service.spec.ts @@ -1,6 +1,6 @@ import { Test } from '@nestjs/testing'; -import { CatsService } from './cats.service'; -import { Cat } from './interfaces/cat.interface'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; describe('CatsService', () => { let catsService: CatsService; diff --git a/sample/01-cats-app/src/cats/cats.service.ts b/sample/01-cats-app/src/cats/cats.service.ts index 955f564087d..497c33de5a5 100644 --- a/sample/01-cats-app/src/cats/cats.service.ts +++ b/sample/01-cats-app/src/cats/cats.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Cat } from './interfaces/cat.interface'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable() export class CatsService { diff --git a/sample/01-cats-app/src/common/guards/roles.guard.ts b/sample/01-cats-app/src/common/guards/roles.guard.ts index 59636afa8a2..439d6081967 100644 --- a/sample/01-cats-app/src/common/guards/roles.guard.ts +++ b/sample/01-cats-app/src/common/guards/roles.guard.ts @@ -1,6 +1,6 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { Roles } from '../decorators/roles.decorator'; +import { Roles } from '../decorators/roles.decorator.js'; @Injectable() export class RolesGuard implements CanActivate { @@ -14,7 +14,7 @@ export class RolesGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const user = request.user; const hasRole = () => - user.roles.some(role => !!roles.find(item => item === role)); + user.roles.some((role: string) => !!roles.find(item => item === role)); return user && user.roles && hasRole(); } diff --git a/sample/01-cats-app/src/core/core.module.ts b/sample/01-cats-app/src/core/core.module.ts index c6e9a519ff0..19f3aa3829e 100644 --- a/sample/01-cats-app/src/core/core.module.ts +++ b/sample/01-cats-app/src/core/core.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; -import { LoggingInterceptor } from './interceptors/logging.interceptor'; -import { TransformInterceptor } from './interceptors/transform.interceptor'; +import { LoggingInterceptor } from './interceptors/logging.interceptor.js'; +import { TransformInterceptor } from './interceptors/transform.interceptor.js'; @Module({ providers: [ diff --git a/sample/01-cats-app/src/core/interceptors/transform.interceptor.ts b/sample/01-cats-app/src/core/interceptors/transform.interceptor.ts index c84c6428f5e..b08f31f221d 100644 --- a/sample/01-cats-app/src/core/interceptors/transform.interceptor.ts +++ b/sample/01-cats-app/src/core/interceptors/transform.interceptor.ts @@ -12,9 +12,10 @@ export interface Response { } @Injectable() -export class TransformInterceptor - implements NestInterceptor> -{ +export class TransformInterceptor implements NestInterceptor< + T, + Response +> { intercept( context: ExecutionContext, next: CallHandler, diff --git a/sample/01-cats-app/src/main.ts b/sample/01-cats-app/src/main.ts index ac2486c640c..261ee54ad60 100644 --- a/sample/01-cats-app/src/main.ts +++ b/sample/01-cats-app/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/01-cats-app/tsconfig.json b/sample/01-cats-app/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/01-cats-app/tsconfig.json +++ b/sample/01-cats-app/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/01-cats-app/vitest.config.e2e.mts b/sample/01-cats-app/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/01-cats-app/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/01-cats-app/vitest.config.mts b/sample/01-cats-app/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/01-cats-app/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/02-gateways/e2e/events-gateway/gateway.e2e-spec.ts b/sample/02-gateways/e2e/events-gateway/gateway.e2e-spec.ts index c9dbfd51e96..66c6c9b5da0 100644 --- a/sample/02-gateways/e2e/events-gateway/gateway.e2e-spec.ts +++ b/sample/02-gateways/e2e/events-gateway/gateway.e2e-spec.ts @@ -1,6 +1,6 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { AppModule } from '../../src/app.module'; +import { AppModule } from '../../src/app.module.js'; import { io, Socket } from 'socket.io-client'; describe('EventsGateway', () => { @@ -16,31 +16,46 @@ describe('EventsGateway', () => { await app.listen(3000); }); - beforeEach(done => { - socket = io('http://localhost:3000'); - socket.on('connect', () => { - done(); + beforeEach(() => { + return new Promise(resolve => { + socket = io('http://localhost:3000'); + socket.on('connect', () => { + resolve(); + }); }); }); describe('findAll', () => { - it('should receive 3 numbers', done => { - let eventCount = 1; - socket.emit('events', { test: 'test' }); - socket.on('events', data => { - expect(data).toBe(eventCount); - if (++eventCount > 3) { - done(); - } + it('should receive 3 numbers', () => { + return new Promise((resolve, reject) => { + let eventCount = 1; + socket.emit('events', { test: 'test' }); + socket.on('events', data => { + try { + expect(data).toBe(eventCount); + } catch (err) { + reject(err); + return; + } + if (++eventCount > 3) { + resolve(); + } + }); }); }); }); describe('identity', () => { - it('should return the same number has what was sent', done => { - socket.emit('identity', 0, response => { - expect(response).toBe(0); - done(); + it('should return the same number has what was sent', () => { + return new Promise((resolve, reject) => { + socket.emit('identity', 0, response => { + try { + expect(response).toBe(0); + resolve(); + } catch (err) { + reject(err); + } + }); }); }); }); diff --git a/sample/02-gateways/e2e/jest-e2e.json b/sample/02-gateways/e2e/jest-e2e.json deleted file mode 100644 index 72eb5c3c0a3..00000000000 --- a/sample/02-gateways/e2e/jest-e2e.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ], - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", - "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], - "coverageReporters": ["json", "lcov"] -} \ No newline at end of file diff --git a/sample/02-gateways/eslint.config.mjs b/sample/02-gateways/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/02-gateways/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/02-gateways/package.json b/sample/02-gateways/package.json index bc795195a92..0a5d64409c2 100644 --- a/sample/02-gateways/package.json +++ b/sample/02-gateways/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./e2e/jest-e2e.json" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -33,45 +33,22 @@ "socket.io": "4.8.3" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", "@types/ws": "8.5.13", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "redis": "5.10.0", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/02-gateways/src/app.module.ts b/sample/02-gateways/src/app.module.ts index a5d9e1125d8..dded5147a9e 100644 --- a/sample/02-gateways/src/app.module.ts +++ b/sample/02-gateways/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { EventsModule } from './events/events.module'; +import { EventsModule } from './events/events.module.js'; @Module({ imports: [EventsModule], diff --git a/sample/02-gateways/src/events/events.gateway.spec.ts b/sample/02-gateways/src/events/events.gateway.spec.ts index e40de3debf0..15972284a5c 100644 --- a/sample/02-gateways/src/events/events.gateway.spec.ts +++ b/sample/02-gateways/src/events/events.gateway.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { reduce } from 'rxjs/operators'; -import { EventsGateway } from './events.gateway'; +import { EventsGateway } from './events.gateway.js'; describe('EventsGateway', () => { let gateway: EventsGateway; @@ -18,17 +18,19 @@ describe('EventsGateway', () => { }); describe('findAll', () => { - it('should return 3 numbers', done => { - gateway - .findAll({}) - .pipe(reduce((acc, item) => [...acc, item], [])) - .subscribe(results => { - expect(results.length).toBe(3); - results.forEach((result, index) => - expect(result.data).toBe(index + 1), - ); - done(); - }); + it('should return 3 numbers', () => { + return new Promise(resolve => { + gateway + .findAll({}) + .pipe(reduce((acc, item) => [...acc, item], [])) + .subscribe(results => { + expect(results.length).toBe(3); + results.forEach((result, index) => + expect(result.data).toBe(index + 1), + ); + resolve(); + }); + }); }); }); diff --git a/sample/02-gateways/src/events/events.module.ts b/sample/02-gateways/src/events/events.module.ts index 2b2d1cbb36d..bf4644469bf 100644 --- a/sample/02-gateways/src/events/events.module.ts +++ b/sample/02-gateways/src/events/events.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { EventsGateway } from './events.gateway'; +import { EventsGateway } from './events.gateway.js'; @Module({ providers: [EventsGateway], diff --git a/sample/02-gateways/src/main.ts b/sample/02-gateways/src/main.ts index 02ced27bc9a..0826477131d 100644 --- a/sample/02-gateways/src/main.ts +++ b/sample/02-gateways/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -12,4 +12,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/02-gateways/tsconfig.json b/sample/02-gateways/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/02-gateways/tsconfig.json +++ b/sample/02-gateways/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/02-gateways/vitest.config.e2e.mts b/sample/02-gateways/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/02-gateways/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/02-gateways/vitest.config.mts b/sample/02-gateways/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/02-gateways/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/03-microservices/e2e/math/math.e2e-spec.ts b/sample/03-microservices/e2e/math/math.e2e-spec.ts new file mode 100644 index 00000000000..d5c19824e2e --- /dev/null +++ b/sample/03-microservices/e2e/math/math.e2e-spec.ts @@ -0,0 +1,34 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { MicroserviceOptions, Transport } from '@nestjs/microservices'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('MathController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + app.connectMicroservice({ + transport: Transport.TCP, + options: { retryAttempts: 5, retryDelay: 3000 }, + }); + + await app.startAllMicroservices(); + await app.init(); + }, 30000); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return 200', async () => { + await request(app.getHttpServer()).get('/').expect(200); + }); + }); +}); diff --git a/sample/03-microservices/eslint.config.mjs b/sample/03-microservices/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/03-microservices/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/03-microservices/package.json b/sample/03-microservices/package.json index f53aca52c7d..bb735c3772f 100644 --- a/sample/03-microservices/package.json +++ b/sample/03-microservices/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -30,8 +30,6 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", @@ -39,17 +37,14 @@ "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/03-microservices/src/app.module.ts b/sample/03-microservices/src/app.module.ts index 13254190a5c..48c4a007cce 100644 --- a/sample/03-microservices/src/app.module.ts +++ b/sample/03-microservices/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { MathModule } from './math/math.module'; +import { MathModule } from './math/math.module.js'; @Module({ imports: [MathModule], diff --git a/sample/03-microservices/src/main.ts b/sample/03-microservices/src/main.ts index 45fedcdb4c5..e1a3626f45f 100644 --- a/sample/03-microservices/src/main.ts +++ b/sample/03-microservices/src/main.ts @@ -1,6 +1,6 @@ import { NestFactory } from '@nestjs/core'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { /** @@ -24,4 +24,4 @@ async function bootstrap() { await app.listen(3001); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/03-microservices/src/math/math.controller.spec.ts b/sample/03-microservices/src/math/math.controller.spec.ts new file mode 100644 index 00000000000..958285974e1 --- /dev/null +++ b/sample/03-microservices/src/math/math.controller.spec.ts @@ -0,0 +1,50 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MathController } from './math.controller.js'; +import { MATH_SERVICE } from './math.constants.js'; +import { of } from 'rxjs'; + +describe('MathController', () => { + let controller: MathController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MathController], + providers: [ + { + provide: MATH_SERVICE, + useValue: { + send: vi.fn().mockReturnValue(of(15)), + }, + }, + ], + }).compile(); + + controller = module.get(MathController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + describe('execute()', () => { + it('should send sum pattern and return result', () => { + return new Promise(resolve => { + const result = controller.execute(); + result.subscribe(value => { + expect(value).toBe(15); + resolve(); + }); + }); + }); + }); + + describe('sum()', () => { + it('should return sum of numbers', () => { + expect(controller.sum([1, 2, 3, 4, 5])).toBe(15); + }); + + it('should handle empty array', () => { + expect(() => controller.sum([])).toThrow(); + }); + }); +}); diff --git a/sample/03-microservices/src/math/math.controller.ts b/sample/03-microservices/src/math/math.controller.ts index d1859ef46d1..15239502a6a 100644 --- a/sample/03-microservices/src/math/math.controller.ts +++ b/sample/03-microservices/src/math/math.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Inject } from '@nestjs/common'; import { ClientProxy, MessagePattern } from '@nestjs/microservices'; import { Observable } from 'rxjs'; -import { MATH_SERVICE } from './math.constants'; +import { MATH_SERVICE } from './math.constants.js'; @Controller() export class MathController { diff --git a/sample/03-microservices/src/math/math.module.ts b/sample/03-microservices/src/math/math.module.ts index d1f856e1f55..6a2831b73d2 100644 --- a/sample/03-microservices/src/math/math.module.ts +++ b/sample/03-microservices/src/math/math.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { ClientsModule, Transport } from '@nestjs/microservices'; -import { MATH_SERVICE } from './math.constants'; -import { MathController } from './math.controller'; +import { MATH_SERVICE } from './math.constants.js'; +import { MathController } from './math.controller.js'; @Module({ imports: [ diff --git a/sample/03-microservices/tsconfig.json b/sample/03-microservices/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/03-microservices/tsconfig.json +++ b/sample/03-microservices/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/03-microservices/vitest.config.e2e.mts b/sample/03-microservices/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/03-microservices/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/03-microservices/vitest.config.mts b/sample/03-microservices/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/03-microservices/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/04-grpc/e2e/hero/hero.e2e-spec.ts b/sample/04-grpc/e2e/hero/hero.e2e-spec.ts new file mode 100644 index 00000000000..ff55b3a9e65 --- /dev/null +++ b/sample/04-grpc/e2e/hero/hero.e2e-spec.ts @@ -0,0 +1,51 @@ +import { INestApplication } from '@nestjs/common'; +import { MicroserviceOptions } from '@nestjs/microservices'; +import { Test } from '@nestjs/testing'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; +import { grpcClientOptions } from '../../src/grpc-client.options.js'; + +describe('HeroController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleRef.createNestApplication(); + app.connectMicroservice(grpcClientOptions); + + await app.startAllMicroservices(); + await app.listen(3001); + }, 30000); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /hero/:id', () => { + it('should return a hero by id', async () => { + const response = await request(app.getHttpServer()) + .get('/hero/1') + .expect(200); + + expect(response.body).toMatchObject({ id: 1, name: 'John' }); + }); + }); + + describe('GET /hero', () => { + it('should return multiple heroes', async () => { + const response = await request(app.getHttpServer()) + .get('/hero') + .expect(200); + + expect(response.body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: 1, name: 'John' }), + expect.objectContaining({ id: 2, name: 'Doe' }), + ]), + ); + }); + }); +}); diff --git a/sample/04-grpc/eslint.config.mjs b/sample/04-grpc/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/04-grpc/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/04-grpc/package.json b/sample/04-grpc/package.json index a5fb096e462..010273a2503 100644 --- a/sample/04-grpc/package.json +++ b/sample/04-grpc/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@grpc/grpc-js": "1.14.3", @@ -32,25 +32,20 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/04-grpc/src/app.module.ts b/sample/04-grpc/src/app.module.ts index 5dccbc032b1..340b352caa4 100644 --- a/sample/04-grpc/src/app.module.ts +++ b/sample/04-grpc/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { HeroModule } from './hero/hero.module'; +import { HeroModule } from './hero/hero.module.js'; @Module({ imports: [HeroModule], diff --git a/sample/04-grpc/src/grpc-client.options.ts b/sample/04-grpc/src/grpc-client.options.ts index abdae9a88bd..da06011b0bc 100644 --- a/sample/04-grpc/src/grpc-client.options.ts +++ b/sample/04-grpc/src/grpc-client.options.ts @@ -1,13 +1,16 @@ import { ReflectionService } from '@grpc/reflection'; import { GrpcOptions, Transport } from '@nestjs/microservices'; -import { join } from 'path'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); export const grpcClientOptions: GrpcOptions = { transport: Transport.GRPC, options: { package: 'hero', // ['hero', 'hero2'] protoPath: join(__dirname, './hero/hero.proto'), // ['./hero/hero.proto', './hero/hero2.proto'] - onLoadPackageDefinition: (pkg, server) => { + onLoadPackageDefinition: (pkg: any, server: any) => { new ReflectionService(pkg).addToServer(server); }, }, diff --git a/sample/04-grpc/src/hero/hero.controller.ts b/sample/04-grpc/src/hero/hero.controller.ts index 796a5e6a610..14582e09a5e 100644 --- a/sample/04-grpc/src/hero/hero.controller.ts +++ b/sample/04-grpc/src/hero/hero.controller.ts @@ -6,8 +6,8 @@ import { } from '@nestjs/microservices'; import { Observable, ReplaySubject, Subject } from 'rxjs'; import { toArray } from 'rxjs/operators'; -import { HeroById } from './interfaces/hero-by-id.interface'; -import { Hero } from './interfaces/hero.interface'; +import { HeroById } from './interfaces/hero-by-id.interface.js'; +import { Hero } from './interfaces/hero.interface.js'; interface HeroesService { findOne(data: HeroById): Observable; @@ -46,7 +46,7 @@ export class HeroController implements OnModuleInit { @GrpcMethod('HeroesService') findOne(data: HeroById): Hero { - return this.items.find(({ id }) => id === data.id); + return this.items.find(({ id }) => id === data.id)!; } @GrpcStreamMethod('HeroesService') @@ -54,7 +54,7 @@ export class HeroController implements OnModuleInit { const hero$ = new Subject(); const onNext = (heroById: HeroById) => { - const item = this.items.find(({ id }) => id === heroById.id); + const item = this.items.find(({ id }) => id === heroById.id)!; hero$.next(item); }; const onComplete = () => hero$.complete(); diff --git a/sample/04-grpc/src/hero/hero.module.ts b/sample/04-grpc/src/hero/hero.module.ts index 85dc4f3e440..fe9c432c2a0 100644 --- a/sample/04-grpc/src/hero/hero.module.ts +++ b/sample/04-grpc/src/hero/hero.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { ClientsModule } from '@nestjs/microservices'; -import { grpcClientOptions } from '../grpc-client.options'; -import { HeroController } from './hero.controller'; +import { grpcClientOptions } from '../grpc-client.options.js'; +import { HeroController } from './hero.controller.js'; @Module({ imports: [ diff --git a/sample/04-grpc/src/main.ts b/sample/04-grpc/src/main.ts index 2955a15a938..d1286480bd7 100644 --- a/sample/04-grpc/src/main.ts +++ b/sample/04-grpc/src/main.ts @@ -1,7 +1,7 @@ import { NestFactory } from '@nestjs/core'; import { MicroserviceOptions } from '@nestjs/microservices'; -import { AppModule } from './app.module'; -import { grpcClientOptions } from './grpc-client.options'; +import { AppModule } from './app.module.js'; +import { grpcClientOptions } from './grpc-client.options.js'; async function bootstrap() { /** @@ -25,4 +25,4 @@ async function bootstrap() { await app.listen(3001); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/04-grpc/tsconfig.json b/sample/04-grpc/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/04-grpc/tsconfig.json +++ b/sample/04-grpc/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/04-grpc/vitest.config.e2e.mts b/sample/04-grpc/vitest.config.e2e.mts new file mode 100644 index 00000000000..0f98631f448 --- /dev/null +++ b/sample/04-grpc/vitest.config.e2e.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + hookTimeout: 30000, + } +}); diff --git a/sample/04-grpc/vitest.config.mts b/sample/04-grpc/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/04-grpc/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/05-sql-typeorm/e2e/users/users.e2e-spec.ts b/sample/05-sql-typeorm/e2e/users/users.e2e-spec.ts new file mode 100644 index 00000000000..d8ed07a1efa --- /dev/null +++ b/sample/05-sql-typeorm/e2e/users/users.e2e-spec.ts @@ -0,0 +1,145 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import request from 'supertest'; +import { UsersModule } from '../../src/users/users.module.js'; +import { User } from '../../src/users/user.entity.js'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +describe('UsersController (e2e)', () => { + let app: INestApplication; + let userRepository: Repository; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [ + TypeOrmModule.forRoot({ + type: 'sqlite', + database: ':memory:', + entities: [User], + synchronize: true, + }), + UsersModule, + ], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + + userRepository = moduleFixture.get>( + getRepositoryToken(User), + ); + }, 30000); + + afterEach(async () => { + await userRepository.clear(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('POST /users', () => { + it('should create a new user and return it', async () => { + const createUserDto = { firstName: 'John', lastName: 'Doe' }; + + const response = await request(app.getHttpServer()) + .post('/users') + .send(createUserDto) + .expect(201); + + expect(response.body).toMatchObject(createUserDto); + expect(response.body.id).toBeDefined(); + }); + + it('should persist the user to the database', async () => { + const createUserDto = { firstName: 'Jane', lastName: 'Doe' }; + + await request(app.getHttpServer()) + .post('/users') + .send(createUserDto) + .expect(201); + + const userInDb = await userRepository.findOneBy({ firstName: 'Jane' }); + expect(userInDb).not.toBeNull(); + expect(userInDb?.lastName).toBe('Doe'); + }); + }); + + describe('GET /users', () => { + it('should return an empty array when no users exist', async () => { + const response = await request(app.getHttpServer()) + .get('/users') + .expect(200); + + expect(response.body).toEqual([]); + }); + + it('should return all users', async () => { + await userRepository.save([ + { firstName: 'John', lastName: 'Doe' }, + { firstName: 'Jane', lastName: 'Doe' }, + ]); + + const response = await request(app.getHttpServer()) + .get('/users') + .expect(200); + + expect(response.body).toHaveLength(2); + expect(response.body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ firstName: 'John', lastName: 'Doe' }), + expect.objectContaining({ firstName: 'Jane', lastName: 'Doe' }), + ]), + ); + }); + }); + + describe('GET /users/:id', () => { + it('should return a single user by id', async () => { + const createdUser = await userRepository.save({ + firstName: 'John', + lastName: 'Doe', + }); + + const response = await request(app.getHttpServer()) + .get(`/users/${createdUser.id}`) + .expect(200); + + expect(response.body).toMatchObject({ + firstName: 'John', + lastName: 'Doe', + }); + }); + }); + + describe('DELETE /users/:id', () => { + it('should delete a user', async () => { + const createdUser = await userRepository.save({ + firstName: 'John', + lastName: 'Doe', + }); + + await request(app.getHttpServer()) + .delete(`/users/${createdUser.id}`) + .expect(200); + }); + + it('should remove the user from the database', async () => { + const createdUser = await userRepository.save({ + firstName: 'John', + lastName: 'Doe', + }); + + await request(app.getHttpServer()) + .delete(`/users/${createdUser.id}`) + .expect(200); + + const deletedUser = await userRepository.findOneBy({ + id: createdUser.id, + }); + expect(deletedUser).toBeNull(); + }); + }); +}); diff --git a/sample/05-sql-typeorm/eslint.config.mjs b/sample/05-sql-typeorm/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/05-sql-typeorm/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/05-sql-typeorm/package.json b/sample/05-sql-typeorm/package.json index 475890e364a..7ab70a87ba9 100644 --- a/sample/05-sql-typeorm/package.json +++ b/sample/05-sql-typeorm/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -30,43 +30,22 @@ "typeorm": "0.3.28" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", + "@types/sqlite3": "^3.1.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", + "sqlite3": "^5.1.7", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/05-sql-typeorm/src/app.module.ts b/sample/05-sql-typeorm/src/app.module.ts index d6da69ee993..373b8d1e297 100644 --- a/sample/05-sql-typeorm/src/app.module.ts +++ b/sample/05-sql-typeorm/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { UsersModule } from './users/users.module'; +import { UsersModule } from './users/users.module.js'; @Module({ imports: [ diff --git a/sample/05-sql-typeorm/src/main.ts b/sample/05-sql-typeorm/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/05-sql-typeorm/src/main.ts +++ b/sample/05-sql-typeorm/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/05-sql-typeorm/src/users/users.controller.spec.ts b/sample/05-sql-typeorm/src/users/users.controller.spec.ts index 4d1160037c3..63456d38ad0 100644 --- a/sample/05-sql-typeorm/src/users/users.controller.spec.ts +++ b/sample/05-sql-typeorm/src/users/users.controller.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { CreateUserDto } from './dto/create-user.dto'; -import { UsersController } from './users.controller'; -import { UsersService } from './users.service'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { UsersController } from './users.controller.js'; +import { UsersService } from './users.service.js'; const createUserDto: CreateUserDto = { firstName: 'firstName #1', @@ -20,12 +20,12 @@ describe('UsersController', () => { { provide: UsersService, useValue: { - create: jest + create: vi .fn() .mockImplementation((user: CreateUserDto) => Promise.resolve({ id: '1', ...user }), ), - findAll: jest.fn().mockResolvedValue([ + findAll: vi.fn().mockResolvedValue([ { firstName: 'firstName #1', lastName: 'lastName #1', @@ -35,14 +35,14 @@ describe('UsersController', () => { lastName: 'lastName #2', }, ]), - findOne: jest.fn().mockImplementation((id: string) => + findOne: vi.fn().mockImplementation((id: string) => Promise.resolve({ firstName: 'firstName #1', lastName: 'lastName #1', id, }), ), - remove: jest.fn(), + remove: vi.fn(), }, }, ], diff --git a/sample/05-sql-typeorm/src/users/users.controller.ts b/sample/05-sql-typeorm/src/users/users.controller.ts index 4f88099cdd9..fd88ce60476 100644 --- a/sample/05-sql-typeorm/src/users/users.controller.ts +++ b/sample/05-sql-typeorm/src/users/users.controller.ts @@ -7,9 +7,9 @@ import { Post, ParseIntPipe, } from '@nestjs/common'; -import { CreateUserDto } from './dto/create-user.dto'; -import { User } from './user.entity'; -import { UsersService } from './users.service'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { User } from './user.entity.js'; +import { UsersService } from './users.service.js'; @Controller('users') export class UsersController { @@ -26,7 +26,7 @@ export class UsersController { } @Get(':id') - findOne(@Param('id', ParseIntPipe) id: number): Promise { + findOne(@Param('id', ParseIntPipe) id: number): Promise { return this.usersService.findOne(id); } diff --git a/sample/05-sql-typeorm/src/users/users.module.ts b/sample/05-sql-typeorm/src/users/users.module.ts index a2227423066..9d9288a1e5a 100644 --- a/sample/05-sql-typeorm/src/users/users.module.ts +++ b/sample/05-sql-typeorm/src/users/users.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { User } from './user.entity'; -import { UsersController } from './users.controller'; -import { UsersService } from './users.service'; +import { User } from './user.entity.js'; +import { UsersController } from './users.controller.js'; +import { UsersService } from './users.service.js'; @Module({ imports: [TypeOrmModule.forFeature([User])], diff --git a/sample/05-sql-typeorm/src/users/users.service.spec.ts b/sample/05-sql-typeorm/src/users/users.service.spec.ts index 4d6e1e08b7d..250c5f0895c 100644 --- a/sample/05-sql-typeorm/src/users/users.service.spec.ts +++ b/sample/05-sql-typeorm/src/users/users.service.spec.ts @@ -1,8 +1,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { User } from './user.entity'; -import { UsersService } from './users.service'; +import { User } from './user.entity.js'; +import { UsersService } from './users.service.js'; const userArray = [ { @@ -31,11 +31,11 @@ describe('UserService', () => { { provide: getRepositoryToken(User), useValue: { - find: jest.fn().mockResolvedValue(userArray), - findOneBy: jest.fn().mockResolvedValue(oneUser), - save: jest.fn().mockResolvedValue(oneUser), - remove: jest.fn(), - delete: jest.fn(), + find: vi.fn().mockResolvedValue(userArray), + findOneBy: vi.fn().mockResolvedValue(oneUser), + save: vi.fn().mockResolvedValue(oneUser), + remove: vi.fn(), + delete: vi.fn(), }, }, ], @@ -74,7 +74,7 @@ describe('UserService', () => { describe('findOne()', () => { it('should get a single user', () => { - const repoSpy = jest.spyOn(repository, 'findOneBy'); + const repoSpy = vi.spyOn(repository, 'findOneBy'); expect(service.findOne(1)).resolves.toEqual(oneUser); expect(repoSpy).toHaveBeenCalledWith({ id: 1 }); }); @@ -82,7 +82,7 @@ describe('UserService', () => { describe('remove()', () => { it('should call remove with the passed value', async () => { - const removeSpy = jest.spyOn(repository, 'delete'); + const removeSpy = vi.spyOn(repository, 'delete'); const retVal = await service.remove('2'); expect(removeSpy).toHaveBeenCalledWith('2'); expect(retVal).toBeUndefined(); diff --git a/sample/05-sql-typeorm/src/users/users.service.ts b/sample/05-sql-typeorm/src/users/users.service.ts index ac82eb7e1a1..47ec8abff7d 100644 --- a/sample/05-sql-typeorm/src/users/users.service.ts +++ b/sample/05-sql-typeorm/src/users/users.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { CreateUserDto } from './dto/create-user.dto'; -import { User } from './user.entity'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { User } from './user.entity.js'; @Injectable() export class UsersService { @@ -23,7 +23,7 @@ export class UsersService { return this.usersRepository.find(); } - findOne(id: number): Promise { + findOne(id: number): Promise { return this.usersRepository.findOneBy({ id: id }); } diff --git a/sample/05-sql-typeorm/test/jest-e2e.json b/sample/05-sql-typeorm/test/jest-e2e.json deleted file mode 100644 index e9d912f3e3c..00000000000 --- a/sample/05-sql-typeorm/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/sample/05-sql-typeorm/test/users/users.e2e-spec.ts b/sample/05-sql-typeorm/test/users/users.e2e-spec.ts index 8f5a67caf5d..61f1352c7b5 100644 --- a/sample/05-sql-typeorm/test/users/users.e2e-spec.ts +++ b/sample/05-sql-typeorm/test/users/users.e2e-spec.ts @@ -1,9 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { TypeOrmModule } from '@nestjs/typeorm'; -import * as request from 'supertest'; -import { CreateUserDto } from '../../src/users/dto/create-user.dto'; -import { UsersModule } from '../../src/users/users.module'; +import request from 'supertest'; +import { CreateUserDto } from '../../src/users/dto/create-user.dto.js'; +import { UsersModule } from '../../src/users/users.module.js'; describe('Users - /users (e2e)', () => { const users = { diff --git a/sample/05-sql-typeorm/tsconfig.json b/sample/05-sql-typeorm/tsconfig.json index 29bde718a4f..697ff9b7000 100644 --- a/sample/05-sql-typeorm/tsconfig.json +++ b/sample/05-sql-typeorm/tsconfig.json @@ -1,17 +1,25 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*", "test/**/*"], + "include": [ + "src/**/*", + "test/**/*" + ] } diff --git a/sample/05-sql-typeorm/vitest.config.e2e.mts b/sample/05-sql-typeorm/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/05-sql-typeorm/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/05-sql-typeorm/vitest.config.mts b/sample/05-sql-typeorm/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/05-sql-typeorm/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/06-mongoose/e2e/cats/cats.e2e-spec.ts b/sample/06-mongoose/e2e/cats/cats.e2e-spec.ts new file mode 100644 index 00000000000..9c3f2d077ba --- /dev/null +++ b/sample/06-mongoose/e2e/cats/cats.e2e-spec.ts @@ -0,0 +1,181 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { getModelToken } from '@nestjs/mongoose'; +import { MongooseModule } from '@nestjs/mongoose'; +import { MongoMemoryServer } from 'mongodb-memory-server'; +import * as mongoose from 'mongoose'; +import request from 'supertest'; +import { CatsModule } from '../../src/cats/cats.module.js'; +import { Cat } from '../../src/cats/schemas/cat.schema.js'; +import { Model } from 'mongoose'; + +describe('CatsController (e2e)', () => { + let app: INestApplication; + let mongoServer: MongoMemoryServer; + let catModel: Model; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [MongooseModule.forRoot(mongoUri), CatsModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + + catModel = moduleFixture.get>(getModelToken(Cat.name)); + }, 30000); + + afterEach(async () => { + await catModel.deleteMany({}); + }); + + afterAll(async () => { + await app.close(); + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + describe('POST /cats', () => { + it('should create a new cat and return it', async () => { + const createCatDto = { name: 'Whiskers', age: 3, breed: 'Persian' }; + + const response = await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + + expect(response.body).toMatchObject(createCatDto); + expect(response.body._id).toBeDefined(); + }); + + it('should persist the cat to the database', async () => { + const createCatDto = { name: 'Luna', age: 2, breed: 'Siamese' }; + + await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + + const catInDb = await catModel.findOne({ name: 'Luna' }); + expect(catInDb).not.toBeNull(); + expect(catInDb?.breed).toBe('Siamese'); + }); + }); + + describe('GET /cats', () => { + it('should return an empty array when no cats exist', async () => { + const response = await request(app.getHttpServer()) + .get('/cats') + .expect(200); + + expect(response.body).toEqual([]); + }); + + it('should return all cats', async () => { + await catModel.create([ + { name: 'Whiskers', age: 3, breed: 'Persian' }, + { name: 'Luna', age: 2, breed: 'Siamese' }, + ]); + + const response = await request(app.getHttpServer()) + .get('/cats') + .expect(200); + + expect(response.body).toHaveLength(2); + expect(response.body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'Whiskers', breed: 'Persian' }), + expect.objectContaining({ name: 'Luna', breed: 'Siamese' }), + ]), + ); + }); + }); + + describe('GET /cats/:id', () => { + it('should return a single cat by id', async () => { + const createdCat = await catModel.create({ + name: 'Whiskers', + age: 3, + breed: 'Persian', + }); + + const response = await request(app.getHttpServer()) + .get(`/cats/${createdCat._id}`) + .expect(200); + + expect(response.body).toMatchObject({ + name: 'Whiskers', + age: 3, + breed: 'Persian', + }); + }); + }); + + describe('POST /cats/:id (update)', () => { + it('should update a cat and return the updated document', async () => { + const createdCat = await catModel.create({ + name: 'Whiskers', + age: 3, + breed: 'Persian', + }); + + const response = await request(app.getHttpServer()) + .post(`/cats/${createdCat._id}`) + .send({ age: 5 }) + .expect(201); + + expect(response.body.age).toBe(5); + expect(response.body.name).toBe('Whiskers'); + }); + + it('should persist the update to the database', async () => { + const createdCat = await catModel.create({ + name: 'Whiskers', + age: 3, + breed: 'Persian', + }); + + await request(app.getHttpServer()) + .post(`/cats/${createdCat._id}`) + .send({ age: 10 }) + .expect(201); + + const updatedCat = await catModel.findById(createdCat._id); + expect(updatedCat?.age).toBe(10); + }); + }); + + describe('DELETE /cats/:id', () => { + it('should delete a cat and return the deleted document', async () => { + const createdCat = await catModel.create({ + name: 'Whiskers', + age: 3, + breed: 'Persian', + }); + + const response = await request(app.getHttpServer()) + .delete(`/cats/${createdCat._id}`) + .expect(200); + + expect(response.body).toMatchObject({ name: 'Whiskers' }); + }); + + it('should remove the cat from the database', async () => { + const createdCat = await catModel.create({ + name: 'Whiskers', + age: 3, + breed: 'Persian', + }); + + await request(app.getHttpServer()) + .delete(`/cats/${createdCat._id}`) + .expect(200); + + const deletedCat = await catModel.findById(createdCat._id); + expect(deletedCat).toBeNull(); + }); + }); +}); diff --git a/sample/06-mongoose/eslint.config.mjs b/sample/06-mongoose/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/06-mongoose/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/06-mongoose/package.json b/sample/06-mongoose/package.json index 51c5e8525b5..96a81764125 100644 --- a/sample/06-mongoose/package.json +++ b/sample/06-mongoose/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -25,47 +25,25 @@ "@nestjs/platform-express": "11.1.13", "mongoose": "9.2.1", "reflect-metadata": "0.2.2", - "rimraf": "6.1.2", + "rimraf": "6.1.3", "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "mongodb-memory-server": "^11.0.1", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/06-mongoose/src/app.module.ts b/sample/06-mongoose/src/app.module.ts index b94eee9af12..4e93ada6c32 100644 --- a/sample/06-mongoose/src/app.module.ts +++ b/sample/06-mongoose/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [ diff --git a/sample/06-mongoose/src/cats/cats.controller.spec.ts b/sample/06-mongoose/src/cats/cats.controller.spec.ts index 298e5cbd39b..3eaf41d7b5c 100644 --- a/sample/06-mongoose/src/cats/cats.controller.spec.ts +++ b/sample/06-mongoose/src/cats/cats.controller.spec.ts @@ -1,20 +1,21 @@ +import { Mocked } from 'vitest'; import { Test, TestingModule } from '@nestjs/testing'; import { Types } from 'mongoose'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; const catsServiceMock = { - create: jest.fn(), - findAll: jest.fn(), - findOne: jest.fn(), - update: jest.fn(), - delete: jest.fn(), + create: vi.fn(), + findAll: vi.fn(), + findOne: vi.fn(), + update: vi.fn(), + delete: vi.fn(), }; describe('CatsController', () => { let controller: CatsController; - let service: jest.Mocked; + let service: Mocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ diff --git a/sample/06-mongoose/src/cats/cats.controller.ts b/sample/06-mongoose/src/cats/cats.controller.ts index deac9a335a3..88e2a3d47fc 100644 --- a/sample/06-mongoose/src/cats/cats.controller.ts +++ b/sample/06-mongoose/src/cats/cats.controller.ts @@ -1,8 +1,8 @@ import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { UpdateCatDto } from './dto/update-cat.dto'; -import { Cat } from './schemas/cat.schema'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { UpdateCatDto } from './dto/update-cat.dto.js'; +import { Cat } from './schemas/cat.schema.js'; @Controller('cats') export class CatsController { @@ -19,7 +19,7 @@ export class CatsController { } @Get(':id') - async findOne(@Param('id') id: string): Promise { + async findOne(@Param('id') id: string): Promise { return this.catsService.findOne(id); } diff --git a/sample/06-mongoose/src/cats/cats.module.ts b/sample/06-mongoose/src/cats/cats.module.ts index 5f2b3a5e561..cda2046f036 100644 --- a/sample/06-mongoose/src/cats/cats.module.ts +++ b/sample/06-mongoose/src/cats/cats.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; -import { Cat, CatSchema } from './schemas/cat.schema'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { Cat, CatSchema } from './schemas/cat.schema.js'; @Module({ imports: [MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])], diff --git a/sample/06-mongoose/src/cats/cats.service.spec.ts b/sample/06-mongoose/src/cats/cats.service.spec.ts index ac2d9badae4..318fdffd6dd 100644 --- a/sample/06-mongoose/src/cats/cats.service.spec.ts +++ b/sample/06-mongoose/src/cats/cats.service.spec.ts @@ -1,21 +1,22 @@ +import { Mocked } from 'vitest'; import { getModelToken } from '@nestjs/mongoose'; import { Test, TestingModule } from '@nestjs/testing'; import { Model, Types } from 'mongoose'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './schemas/cat.schema'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './schemas/cat.schema.js'; const catModelMock = { - create: jest.fn(), - find: jest.fn(), - findOne: jest.fn(), - findByIdAndUpdate: jest.fn(), - findByIdAndDelete: jest.fn(), + create: vi.fn(), + find: vi.fn(), + findOne: vi.fn(), + findByIdAndUpdate: vi.fn(), + findByIdAndDelete: vi.fn(), }; describe('CatsService', () => { let service: CatsService; - let model: jest.Mocked>; + let model: Mocked>; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -72,7 +73,7 @@ describe('CatsService', () => { }, ]; model.find.mockReturnValueOnce({ - exec: jest.fn().mockResolvedValueOnce(mockedCats), + exec: vi.fn().mockResolvedValueOnce(mockedCats), } as any); const result = await service.findAll(); @@ -90,7 +91,7 @@ describe('CatsService', () => { age: 4, }; model.findOne.mockReturnValueOnce({ - exec: jest.fn().mockResolvedValueOnce(mockedCat), + exec: vi.fn().mockResolvedValueOnce(mockedCat), } as any); const id = new Types.ObjectId().toString(); @@ -109,7 +110,7 @@ describe('CatsService', () => { age: 4, }; model.findByIdAndUpdate.mockReturnValueOnce({ - exec: jest.fn().mockResolvedValueOnce(mockedCat), + exec: vi.fn().mockResolvedValueOnce(mockedCat), } as any); const id = new Types.ObjectId().toString(); @@ -137,7 +138,7 @@ describe('CatsService', () => { age: 4, }; model.findByIdAndDelete.mockReturnValueOnce({ - exec: jest.fn().mockResolvedValueOnce(mockedCat), + exec: vi.fn().mockResolvedValueOnce(mockedCat), } as any); const id = new Types.ObjectId().toString(); diff --git a/sample/06-mongoose/src/cats/cats.service.ts b/sample/06-mongoose/src/cats/cats.service.ts index b089e84b194..56c96b69048 100644 --- a/sample/06-mongoose/src/cats/cats.service.ts +++ b/sample/06-mongoose/src/cats/cats.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { UpdateCatDto } from './dto/update-cat.dto'; -import { Cat } from './schemas/cat.schema'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { UpdateCatDto } from './dto/update-cat.dto.js'; +import { Cat } from './schemas/cat.schema.js'; @Injectable() export class CatsService { @@ -18,17 +18,17 @@ export class CatsService { return this.catModel.find().exec(); } - async findOne(id: string): Promise { + async findOne(id: string): Promise { return this.catModel.findOne({ _id: id }).exec(); } - async update(id: string, updateCatDto: UpdateCatDto): Promise { + async update(id: string, updateCatDto: UpdateCatDto): Promise { return this.catModel .findByIdAndUpdate({ _id: id }, updateCatDto, { new: true }) .exec(); } - async delete(id: string): Promise { + async delete(id: string): Promise { const deletedCat = await this.catModel .findByIdAndDelete({ _id: id }) .exec(); diff --git a/sample/06-mongoose/src/main.ts b/sample/06-mongoose/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/06-mongoose/src/main.ts +++ b/sample/06-mongoose/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/06-mongoose/tsconfig.json b/sample/06-mongoose/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/06-mongoose/tsconfig.json +++ b/sample/06-mongoose/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/06-mongoose/vitest.config.e2e.mts b/sample/06-mongoose/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/06-mongoose/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/06-mongoose/vitest.config.mts b/sample/06-mongoose/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/06-mongoose/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/07-sequelize/e2e/users/users.e2e-spec.ts b/sample/07-sequelize/e2e/users/users.e2e-spec.ts new file mode 100644 index 00000000000..9a7d46878b4 --- /dev/null +++ b/sample/07-sequelize/e2e/users/users.e2e-spec.ts @@ -0,0 +1,137 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { SequelizeModule } from '@nestjs/sequelize'; +import request from 'supertest'; +import { UsersModule } from '../../src/users/users.module.js'; +import { User } from '../../src/users/models/user.model.js'; + +describe('UsersController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [ + SequelizeModule.forRoot({ + dialect: 'sqlite', + storage: ':memory:', + models: [User], + autoLoadModels: true, + synchronize: true, + }), + UsersModule, + ], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }, 30000); + + afterEach(async () => { + await User.destroy({ truncate: true }); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('POST /users', () => { + it('should create a new user and return it', async () => { + const createUserDto = { firstName: 'John', lastName: 'Doe' }; + + const response = await request(app.getHttpServer()) + .post('/users') + .send(createUserDto) + .expect(201); + + expect(response.body).toMatchObject(createUserDto); + expect(response.body.id).toBeDefined(); + }); + + it('should persist the user to the database', async () => { + const createUserDto = { firstName: 'Jane', lastName: 'Doe' }; + + await request(app.getHttpServer()) + .post('/users') + .send(createUserDto) + .expect(201); + + const userInDb = await User.findOne({ where: { firstName: 'Jane' } }); + expect(userInDb).not.toBeNull(); + expect(userInDb?.getDataValue('lastName')).toBe('Doe'); + }); + }); + + describe('GET /users', () => { + it('should return an empty array when no users exist', async () => { + const response = await request(app.getHttpServer()) + .get('/users') + .expect(200); + + expect(response.body).toEqual([]); + }); + + it('should return all users', async () => { + await User.create({ firstName: 'John', lastName: 'Doe' }); + await User.create({ firstName: 'Jane', lastName: 'Doe' }); + + const response = await request(app.getHttpServer()) + .get('/users') + .expect(200); + + expect(response.body).toHaveLength(2); + expect(response.body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ firstName: 'John', lastName: 'Doe' }), + expect.objectContaining({ firstName: 'Jane', lastName: 'Doe' }), + ]), + ); + }); + }); + + describe('GET /users/:id', () => { + it('should return a single user by id', async () => { + const createdUser = await User.create({ + firstName: 'John', + lastName: 'Doe', + }); + + const response = await request(app.getHttpServer()) + .get(`/users/${createdUser.id}`) + .expect(200); + + expect(response.body).toMatchObject({ + firstName: 'John', + lastName: 'Doe', + }); + }); + }); + + describe('DELETE /users/:id', () => { + it('should delete a user and return nothing', async () => { + const createdUser = await User.create({ + firstName: 'John', + lastName: 'Doe', + }); + + await request(app.getHttpServer()) + .delete(`/users/${createdUser.id}`) + .expect(200); + }); + + it('should remove the user from the database', async () => { + const createdUser = await User.create({ + firstName: 'John', + lastName: 'Doe', + }); + + await request(app.getHttpServer()) + .delete(`/users/${createdUser.id}`) + .expect(200); + + const deletedUser = await User.findOne({ + where: { id: createdUser.id }, + }); + expect(deletedUser).toBeNull(); + }); + }); +}); diff --git a/sample/07-sequelize/eslint.config.mjs b/sample/07-sequelize/eslint.config.mjs deleted file mode 100644 index b9dc3228945..00000000000 --- a/sample/07-sequelize/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn' - }, - }, -); \ No newline at end of file diff --git a/sample/07-sequelize/package.json b/sample/07-sequelize/package.json index 02269554bac..0742f446b01 100644 --- a/sample/07-sequelize/package.json +++ b/sample/07-sequelize/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -29,46 +29,24 @@ "rxjs": "7.8.2", "sequelize": "6.37.7", "sequelize-typescript": "2.1.6", - "typescript": "5.9.3" + "typescript": "6.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", + "sqlite3": "^5.1.7", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/07-sequelize/src/app.module.ts b/sample/07-sequelize/src/app.module.ts index f7160b78302..2d7464116eb 100644 --- a/sample/07-sequelize/src/app.module.ts +++ b/sample/07-sequelize/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { SequelizeModule } from '@nestjs/sequelize'; -import { UsersModule } from './users/users.module'; +import { UsersModule } from './users/users.module.js'; @Module({ imports: [ diff --git a/sample/07-sequelize/src/main.ts b/sample/07-sequelize/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/07-sequelize/src/main.ts +++ b/sample/07-sequelize/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/07-sequelize/src/users/users.controller.spec.ts b/sample/07-sequelize/src/users/users.controller.spec.ts index b8d40c7cb8e..3d367160e57 100644 --- a/sample/07-sequelize/src/users/users.controller.spec.ts +++ b/sample/07-sequelize/src/users/users.controller.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { CreateUserDto } from './dto/create-user.dto'; -import { UsersController } from './users.controller'; -import { UsersService } from './users.service'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { UsersController } from './users.controller.js'; +import { UsersService } from './users.service.js'; const createUserDto: CreateUserDto = { firstName: 'firstName #1', @@ -19,12 +19,12 @@ describe('UsersController', () => { { provide: UsersService, useValue: { - create: jest + create: vi .fn() .mockImplementation((user: CreateUserDto) => Promise.resolve({ id: '1', ...user }), ), - findAll: jest.fn().mockResolvedValue([ + findAll: vi.fn().mockResolvedValue([ { firstName: 'firstName #1', lastName: 'lastName #1', @@ -34,14 +34,14 @@ describe('UsersController', () => { lastName: 'lastName #2', }, ]), - findOne: jest.fn().mockImplementation((id: string) => + findOne: vi.fn().mockImplementation((id: string) => Promise.resolve({ firstName: 'firstName #1', lastName: 'lastName #1', id, }), ), - remove: jest.fn(), + remove: vi.fn(), }, }, ], diff --git a/sample/07-sequelize/src/users/users.controller.ts b/sample/07-sequelize/src/users/users.controller.ts index 7ab1f48a658..9298743d8be 100644 --- a/sample/07-sequelize/src/users/users.controller.ts +++ b/sample/07-sequelize/src/users/users.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; -import { CreateUserDto } from './dto/create-user.dto'; -import { User } from './models/user.model'; -import { UsersService } from './users.service'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { User } from './models/user.model.js'; +import { UsersService } from './users.service.js'; @Controller('users') export class UsersController { @@ -18,7 +18,7 @@ export class UsersController { } @Get(':id') - findOne(@Param('id') id: string): Promise { + findOne(@Param('id') id: string): Promise { return this.usersService.findOne(id); } diff --git a/sample/07-sequelize/src/users/users.module.ts b/sample/07-sequelize/src/users/users.module.ts index 5276742a36e..13706452dbf 100644 --- a/sample/07-sequelize/src/users/users.module.ts +++ b/sample/07-sequelize/src/users/users.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { SequelizeModule } from '@nestjs/sequelize'; -import { User } from './models/user.model'; -import { UsersController } from './users.controller'; -import { UsersService } from './users.service'; +import { User } from './models/user.model.js'; +import { UsersController } from './users.controller.js'; +import { UsersService } from './users.service.js'; @Module({ imports: [SequelizeModule.forFeature([User])], diff --git a/sample/07-sequelize/src/users/users.service.spec.ts b/sample/07-sequelize/src/users/users.service.spec.ts index 1a5bfb0a63d..4b93ba81e29 100644 --- a/sample/07-sequelize/src/users/users.service.spec.ts +++ b/sample/07-sequelize/src/users/users.service.spec.ts @@ -1,7 +1,7 @@ import { getModelToken } from '@nestjs/sequelize'; import { Test, TestingModule } from '@nestjs/testing'; -import { User } from './models/user.model'; -import { UsersService } from './users.service'; +import { User } from './models/user.model.js'; +import { UsersService } from './users.service.js'; const usersArray = [ { @@ -30,11 +30,11 @@ describe('UserService', () => { { provide: getModelToken(User), useValue: { - findAll: jest.fn(() => usersArray), - findOne: jest.fn(), - create: jest.fn(() => oneUser), - remove: jest.fn(), - destroy: jest.fn(() => oneUser), + findAll: vi.fn(() => usersArray), + findOne: vi.fn(), + create: vi.fn(() => oneUser), + remove: vi.fn(), + destroy: vi.fn(() => oneUser), }, }, ], @@ -72,7 +72,7 @@ describe('UserService', () => { describe('findOne()', () => { it('should get a single user', () => { - const findSpy = jest.spyOn(model, 'findOne'); + const findSpy = vi.spyOn(model, 'findOne'); expect(service.findOne('1')); expect(findSpy).toHaveBeenCalledWith({ where: { id: '1' } }); }); @@ -80,8 +80,8 @@ describe('UserService', () => { describe('remove()', () => { it('should remove a user', async () => { - const findSpy = jest.spyOn(model, 'findOne').mockReturnValue({ - destroy: jest.fn(), + const findSpy = vi.spyOn(model, 'findOne').mockReturnValue({ + destroy: vi.fn(), } as any); const retVal = await service.remove('2'); expect(findSpy).toHaveBeenCalledWith({ where: { id: '2' } }); diff --git a/sample/07-sequelize/src/users/users.service.ts b/sample/07-sequelize/src/users/users.service.ts index 6152525984a..f073d647c88 100644 --- a/sample/07-sequelize/src/users/users.service.ts +++ b/sample/07-sequelize/src/users/users.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/sequelize'; -import { CreateUserDto } from './dto/create-user.dto'; -import { User } from './models/user.model'; +import { CreateUserDto } from './dto/create-user.dto.js'; +import { User } from './models/user.model.js'; @Injectable() export class UsersService { @@ -21,7 +21,7 @@ export class UsersService { return this.userModel.findAll(); } - findOne(id: string): Promise { + findOne(id: string): Promise { return this.userModel.findOne({ where: { id, @@ -31,6 +31,6 @@ export class UsersService { async remove(id: string): Promise { const user = await this.findOne(id); - await user.destroy(); + await user?.destroy(); } } diff --git a/sample/07-sequelize/tsconfig.json b/sample/07-sequelize/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/07-sequelize/tsconfig.json +++ b/sample/07-sequelize/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/07-sequelize/vitest.config.e2e.mts b/sample/07-sequelize/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/07-sequelize/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/07-sequelize/vitest.config.mts b/sample/07-sequelize/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/07-sequelize/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/08-webpack/e2e/app/app.e2e-spec.ts b/sample/08-webpack/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..cfdb974b882 --- /dev/null +++ b/sample/08-webpack/e2e/app/app.e2e-spec.ts @@ -0,0 +1,29 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return the hello message', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.text).toBe('Hello world!'); + }); + }); +}); diff --git a/sample/08-webpack/eslint.config.mjs b/sample/08-webpack/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/08-webpack/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/08-webpack/package.json b/sample/08-webpack/package.json index 3600095d2c4..abcee24957f 100644 --- a/sample/08-webpack/package.json +++ b/sample/08-webpack/package.json @@ -7,9 +7,9 @@ "build": "tsc -p tsconfig.build.json", "build:dev": "nest build --watch --webpack --webpackPath webpack-hmr.config.js", "start": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", + "lint": "oxlint src --fix", "test": "echo 'No tests implemented yet.'", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -17,23 +17,20 @@ "@nestjs/platform-express": "11.1.13", "reflect-metadata": "0.2.2", "rxjs": "7.8.2", - "typescript": "5.9.3" + "typescript": "6.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@types/node": "24.10.13", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", + "oxlint": "1.58.0", "start-server-webpack-plugin": "2.2.5", "ts-loader": "9.5.4", "ts-node": "10.9.2", + "vitest": "4.1.2", "webpack": "5.105.2", "webpack-cli": "6.0.1", - "webpack-node-externals": "3.0.0", - "typescript-eslint": "8.55.0" - } + "webpack-node-externals": "3.0.0" + }, + "type": "module" } diff --git a/sample/08-webpack/src/app.controller.ts b/sample/08-webpack/src/app.controller.ts index cce879ee622..22fbf4d55c3 100644 --- a/sample/08-webpack/src/app.controller.ts +++ b/sample/08-webpack/src/app.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; +import { AppService } from './app.service.js'; @Controller() export class AppController { diff --git a/sample/08-webpack/src/app.module.ts b/sample/08-webpack/src/app.module.ts index 86628031ca2..b4ca839d4c7 100644 --- a/sample/08-webpack/src/app.module.ts +++ b/sample/08-webpack/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { AppController } from './app.controller.js'; +import { AppService } from './app.service.js'; @Module({ imports: [], diff --git a/sample/08-webpack/src/main.ts b/sample/08-webpack/src/main.ts index cabd25f9ad5..713981556ca 100644 --- a/sample/08-webpack/src/main.ts +++ b/sample/08-webpack/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; declare const module: any; @@ -13,4 +13,4 @@ async function bootstrap() { module.hot.dispose(() => app.close()); } } -bootstrap(); +await bootstrap(); diff --git a/sample/08-webpack/tsconfig.json b/sample/08-webpack/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/08-webpack/tsconfig.json +++ b/sample/08-webpack/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/08-webpack/vitest.config.e2e.mts b/sample/08-webpack/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/08-webpack/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/08-webpack/vitest.config.mts b/sample/08-webpack/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/08-webpack/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/09-babel-example/package.json b/sample/09-babel-example/package.json index 311545ecd91..df89e8eb031 100644 --- a/sample/09-babel-example/package.json +++ b/sample/09-babel-example/package.json @@ -8,9 +8,9 @@ "build": "babel src -d dist", "start": "babel-node index.js", "start:dev": "nodemon", - "test": "jest", - "test:cov": "jest --coverage", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "test": "vitest run", + "test:cov": "vitest run --coverage", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -31,18 +31,11 @@ "@babel/register": "7.28.6", "@babel/runtime": "7.28.6", "@nestjs/testing": "11.1.13", - "jest": "30.2.0", "nodemon": "3.1.11", + "oxlint": "1.58.0", "prettier": "3.8.1", - "supertest": "7.2.2" + "supertest": "7.2.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.js$", - "coverageDirectory": "../coverage" - } + "type": "module" } diff --git a/sample/09-babel-example/src/app.module.js b/sample/09-babel-example/src/app.module.js index 77390842912..ce4c4578fa5 100644 --- a/sample/09-babel-example/src/app.module.js +++ b/sample/09-babel-example/src/app.module.js @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [CatsModule], diff --git a/sample/09-babel-example/src/cats/cats.controller.js b/sample/09-babel-example/src/cats/cats.controller.js index 6e08d11292c..cb2fa1e5b8e 100644 --- a/sample/09-babel-example/src/cats/cats.controller.js +++ b/sample/09-babel-example/src/cats/cats.controller.js @@ -7,7 +7,7 @@ import { Dependencies, Param, } from '@nestjs/common'; -import { CatsService } from './cats.service'; +import { CatsService } from './cats.service.js'; @Controller('cats') @Dependencies(CatsService) diff --git a/sample/09-babel-example/src/cats/cats.module.js b/sample/09-babel-example/src/cats/cats.module.js index f3291c7d11e..14b21cd9f14 100644 --- a/sample/09-babel-example/src/cats/cats.module.js +++ b/sample/09-babel-example/src/cats/cats.module.js @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; @Module({ controllers: [CatsController], diff --git a/sample/09-babel-example/src/main.js b/sample/09-babel-example/src/main.js index e1e10d96995..b286f888f25 100644 --- a/sample/09-babel-example/src/main.js +++ b/sample/09-babel-example/src/main.js @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/sample/09-babel-example/vitest.config.mts b/sample/09-babel-example/vitest.config.mts new file mode 100644 index 00000000000..358842a8ab0 --- /dev/null +++ b/sample/09-babel-example/vitest.config.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + testEnvironment: 'node', + include: ['src/**/*.spec.js'], + }, +}); diff --git a/sample/10-fastify/e2e/cats/cats.e2e-spec.ts b/sample/10-fastify/e2e/cats/cats.e2e-spec.ts new file mode 100644 index 00000000000..60115d4b674 --- /dev/null +++ b/sample/10-fastify/e2e/cats/cats.e2e-spec.ts @@ -0,0 +1,90 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import { ValidationPipe } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; +import { RolesGuard } from '../../src/common/guards/roles.guard.js'; +import { CanActivate } from '@nestjs/common'; + +describe('CatsController (e2e)', () => { + let app: NestFastifyApplication; + + const mockRolesGuard: CanActivate = { canActivate: () => true }; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }) + .overrideGuard(RolesGuard) + .useValue(mockRolesGuard) + .compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + app.useGlobalPipes(new ValidationPipe()); + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }, 30000); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /cats', () => { + it('should return an empty array when no cats exist', async () => { + const response = await request(app.getHttpServer()) + .get('/cats') + .expect(200); + + expect(response.body).toMatchObject({ data: [] }); + }); + }); + + describe('POST /cats', () => { + it('should create a cat', async () => { + const createCatDto = { name: 'Whiskers', age: 3, breed: 'Persian' }; + + await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + }); + + it('should return the created cat in findAll', async () => { + const createCatDto = { name: 'Luna', age: 2, breed: 'Siamese' }; + + await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + + const response = await request(app.getHttpServer()) + .get('/cats') + .expect(200); + + expect(response.body.data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'Luna', breed: 'Siamese' }), + ]), + ); + }); + + it('should reject invalid cat data', async () => { + const invalidCatDto = { + name: 'Whiskers', + age: 'not-a-number', + breed: 'Persian', + }; + + await request(app.getHttpServer()) + .post('/cats') + .send(invalidCatDto) + .expect(400); + }); + }); +}); diff --git a/sample/10-fastify/eslint.config.mjs b/sample/10-fastify/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/10-fastify/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/10-fastify/package.json b/sample/10-fastify/package.json index 552908c5daf..408bd4de62f 100644 --- a/sample/10-fastify/package.json +++ b/sample/10-fastify/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -29,24 +29,19 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/10-fastify/src/app.module.ts b/sample/10-fastify/src/app.module.ts index 1a66344a24a..98abd43269a 100644 --- a/sample/10-fastify/src/app.module.ts +++ b/sample/10-fastify/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsModule } from './cats/cats.module'; -import { CoreModule } from './core/core.module'; +import { CatsModule } from './cats/cats.module.js'; +import { CoreModule } from './core/core.module.js'; @Module({ imports: [CatsModule, CoreModule], diff --git a/sample/10-fastify/src/cats/cats.controller.spec.ts b/sample/10-fastify/src/cats/cats.controller.spec.ts new file mode 100644 index 00000000000..3b144d67446 --- /dev/null +++ b/sample/10-fastify/src/cats/cats.controller.spec.ts @@ -0,0 +1,45 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { RolesGuard } from '../common/guards/roles.guard.js'; +import { Reflector } from '@nestjs/core'; + +describe('CatsController', () => { + let controller: CatsController; + let service: CatsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [CatsController], + providers: [CatsService, RolesGuard, Reflector], + }).compile(); + + controller = module.get(CatsController); + service = module.get(CatsService); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + describe('findAll()', () => { + it('should return an array of cats', async () => { + const result: { name: string; age: number; breed: string }[] = [ + { name: 'Whiskers', age: 3, breed: 'Persian' }, + ]; + vi.spyOn(service, 'findAll').mockImplementation(() => result); + + expect(await controller.findAll()).toBe(result); + }); + }); + + describe('create()', () => { + it('should create a cat', async () => { + const createCatDto = { name: 'Whiskers', age: 3, breed: 'Persian' }; + const createSpy = vi.spyOn(service, 'create'); + + await controller.create(createCatDto); + expect(createSpy).toHaveBeenCalledWith(createCatDto); + }); + }); +}); diff --git a/sample/10-fastify/src/cats/cats.controller.ts b/sample/10-fastify/src/cats/cats.controller.ts index 2008e3de796..1b76750048b 100644 --- a/sample/10-fastify/src/cats/cats.controller.ts +++ b/sample/10-fastify/src/cats/cats.controller.ts @@ -1,10 +1,10 @@ import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common'; -import { Roles } from '../common/decorators/roles.decorator'; -import { RolesGuard } from '../common/guards/roles.guard'; -import { ParseIntPipe } from '../common/pipes/parse-int.pipe'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './interfaces/cat.interface'; +import { Roles } from '../common/decorators/roles.decorator.js'; +import { RolesGuard } from '../common/guards/roles.guard.js'; +import { ParseIntPipe } from '../common/pipes/parse-int.pipe.js'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; @UseGuards(RolesGuard) @Controller('cats') diff --git a/sample/10-fastify/src/cats/cats.module.ts b/sample/10-fastify/src/cats/cats.module.ts index f3291c7d11e..14b21cd9f14 100644 --- a/sample/10-fastify/src/cats/cats.module.ts +++ b/sample/10-fastify/src/cats/cats.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; @Module({ controllers: [CatsController], diff --git a/sample/10-fastify/src/cats/cats.service.spec.ts b/sample/10-fastify/src/cats/cats.service.spec.ts new file mode 100644 index 00000000000..51ac829488d --- /dev/null +++ b/sample/10-fastify/src/cats/cats.service.spec.ts @@ -0,0 +1,44 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CatsService } from './cats.service.js'; + +describe('CatsService', () => { + let service: CatsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [CatsService], + }).compile(); + + service = module.get(CatsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + describe('create()', () => { + it('should add a cat to the list', () => { + const cat = { name: 'Whiskers', age: 3, breed: 'Persian' }; + service.create(cat); + + expect(service.findAll()).toContainEqual(cat); + }); + }); + + describe('findAll()', () => { + it('should return an empty array initially', () => { + expect(service.findAll()).toEqual([]); + }); + + it('should return all created cats', () => { + const cat1 = { name: 'Whiskers', age: 3, breed: 'Persian' }; + const cat2 = { name: 'Luna', age: 2, breed: 'Siamese' }; + + service.create(cat1); + service.create(cat2); + + expect(service.findAll()).toHaveLength(2); + expect(service.findAll()).toEqual(expect.arrayContaining([cat1, cat2])); + }); + }); +}); diff --git a/sample/10-fastify/src/cats/cats.service.ts b/sample/10-fastify/src/cats/cats.service.ts index 2619cd7176d..dc5f51e15ec 100644 --- a/sample/10-fastify/src/cats/cats.service.ts +++ b/sample/10-fastify/src/cats/cats.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Cat } from './interfaces/cat.interface'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable() export class CatsService { diff --git a/sample/10-fastify/src/common/guards/roles.guard.ts b/sample/10-fastify/src/common/guards/roles.guard.ts index 8e762522ee2..8db21431078 100644 --- a/sample/10-fastify/src/common/guards/roles.guard.ts +++ b/sample/10-fastify/src/common/guards/roles.guard.ts @@ -13,7 +13,7 @@ export class RolesGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const user = request.user; const hasRole = () => - !!user.roles.find(role => !!roles.find(item => item === role)); + !!user.roles.find((role: string) => !!roles.find(item => item === role)); return user && user.roles && hasRole(); } diff --git a/sample/10-fastify/src/core/core.module.ts b/sample/10-fastify/src/core/core.module.ts index c6e9a519ff0..19f3aa3829e 100644 --- a/sample/10-fastify/src/core/core.module.ts +++ b/sample/10-fastify/src/core/core.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; -import { LoggingInterceptor } from './interceptors/logging.interceptor'; -import { TransformInterceptor } from './interceptors/transform.interceptor'; +import { LoggingInterceptor } from './interceptors/logging.interceptor.js'; +import { TransformInterceptor } from './interceptors/transform.interceptor.js'; @Module({ providers: [ diff --git a/sample/10-fastify/src/core/interceptors/transform.interceptor.ts b/sample/10-fastify/src/core/interceptors/transform.interceptor.ts index c84c6428f5e..b08f31f221d 100644 --- a/sample/10-fastify/src/core/interceptors/transform.interceptor.ts +++ b/sample/10-fastify/src/core/interceptors/transform.interceptor.ts @@ -12,9 +12,10 @@ export interface Response { } @Injectable() -export class TransformInterceptor - implements NestInterceptor> -{ +export class TransformInterceptor implements NestInterceptor< + T, + Response +> { intercept( context: ExecutionContext, next: CallHandler, diff --git a/sample/10-fastify/src/main.ts b/sample/10-fastify/src/main.ts index 938eb31f218..bf299f6683b 100644 --- a/sample/10-fastify/src/main.ts +++ b/sample/10-fastify/src/main.ts @@ -4,7 +4,7 @@ import { FastifyAdapter, NestFastifyApplication, } from '@nestjs/platform-fastify'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create( @@ -15,4 +15,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/10-fastify/tsconfig.json b/sample/10-fastify/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/10-fastify/tsconfig.json +++ b/sample/10-fastify/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/10-fastify/vitest.config.e2e.mts b/sample/10-fastify/vitest.config.e2e.mts new file mode 100644 index 00000000000..0f98631f448 --- /dev/null +++ b/sample/10-fastify/vitest.config.e2e.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + hookTimeout: 30000, + } +}); diff --git a/sample/10-fastify/vitest.config.mts b/sample/10-fastify/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/10-fastify/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/11-swagger/e2e/cats/cats.e2e-spec.ts b/sample/11-swagger/e2e/cats/cats.e2e-spec.ts new file mode 100644 index 00000000000..d63d8254e06 --- /dev/null +++ b/sample/11-swagger/e2e/cats/cats.e2e-spec.ts @@ -0,0 +1,92 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication, ValidationPipe } from '@nestjs/common'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('CatsController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + app.useGlobalPipes(new ValidationPipe()); + + const options = new DocumentBuilder() + .setTitle('Cats example') + .setDescription('The cats API description') + .setVersion('1.0') + .addTag('cats') + .addBearerAuth() + .build(); + const document = SwaggerModule.createDocument(app, options); + SwaggerModule.setup('api', app, document); + + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('POST /cats', () => { + it('should create a cat', async () => { + const createCatDto = { name: 'Whiskers', age: 3, breed: 'Persian' }; + + const response = await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + + expect(response.body).toMatchObject(createCatDto); + }); + + it('should reject invalid cat data', async () => { + await request(app.getHttpServer()) + .post('/cats') + .send({ name: 123, age: 'not-a-number' }) + .expect(400); + }); + }); + + describe('GET /cats/:id', () => { + it('should return a cat by index', async () => { + const createCatDto = { name: 'Milo', age: 2, breed: 'Siamese' }; + + await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + + const response = await request(app.getHttpServer()) + .get('/cats/1') + .expect(200); + + expect(response.body).toMatchObject(createCatDto); + }); + }); + + describe('Swagger documentation', () => { + it('should serve the Swagger JSON document at /api-json', async () => { + const response = await request(app.getHttpServer()) + .get('/api-json') + .expect(200); + + expect(response.body.info.title).toBe('Cats example'); + expect(response.body.info.version).toBe('1.0'); + expect(response.body.paths['/cats']).toBeDefined(); + expect(response.body.paths['/cats/{id}']).toBeDefined(); + }); + + it('should serve the Swagger UI at /api/', async () => { + const response = await request(app.getHttpServer()) + .get('/api/') + .expect(200); + + expect(response.text).toContain('swagger'); + }); + }); +}); diff --git a/sample/11-swagger/eslint.config.mjs b/sample/11-swagger/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/11-swagger/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/11-swagger/package.json b/sample/11-swagger/package.json index 8bb80863fcd..8ac46f470c1 100644 --- a/sample/11-swagger/package.json +++ b/sample/11-swagger/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -30,25 +30,20 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/11-swagger/src/app.module.ts b/sample/11-swagger/src/app.module.ts index 77390842912..ce4c4578fa5 100644 --- a/sample/11-swagger/src/app.module.ts +++ b/sample/11-swagger/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [CatsModule], diff --git a/sample/11-swagger/src/cats/cats.controller.spec.ts b/sample/11-swagger/src/cats/cats.controller.spec.ts new file mode 100644 index 00000000000..8b8cdf2d504 --- /dev/null +++ b/sample/11-swagger/src/cats/cats.controller.spec.ts @@ -0,0 +1,56 @@ +import { vi } from 'vitest'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CreateCatDto } from './dto/create-cat.dto'; +import { Cat } from './entities/cat.entity'; +import { CatsController } from './cats.controller'; +import { CatsService } from './cats.service'; + +describe('CatsController', () => { + let controller: CatsController; + let service: CatsService; + + const cat: Cat = { name: 'Kitty', age: 2, breed: 'Maine Coon' }; + const createCatDto: CreateCatDto = { + name: 'Kitty', + age: 2, + breed: 'Maine Coon', + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [CatsController], + providers: [ + { + provide: CatsService, + useValue: { + create: vi.fn().mockReturnValue(cat), + findOne: vi.fn().mockReturnValue(cat), + }, + }, + ], + }).compile(); + + controller = module.get(CatsController); + service = module.get(CatsService); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + describe('create', () => { + it('should call service.create and return the created cat', async () => { + const result = await controller.create(createCatDto); + expect(service.create).toHaveBeenCalledWith(createCatDto); + expect(result).toEqual(cat); + }); + }); + + describe('findOne', () => { + it('should call service.findOne with parsed id and return the cat', () => { + const result = controller.findOne('0'); + expect(service.findOne).toHaveBeenCalledWith(0); + expect(result).toEqual(cat); + }); + }); +}); diff --git a/sample/11-swagger/src/cats/cats.controller.ts b/sample/11-swagger/src/cats/cats.controller.ts index 30df975535e..c9b7bb5b2ec 100644 --- a/sample/11-swagger/src/cats/cats.controller.ts +++ b/sample/11-swagger/src/cats/cats.controller.ts @@ -5,9 +5,9 @@ import { ApiResponse, ApiTags, } from '@nestjs/swagger'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './entities/cat.entity'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './entities/cat.entity.js'; @ApiBearerAuth() @ApiTags('cats') diff --git a/sample/11-swagger/src/cats/cats.module.ts b/sample/11-swagger/src/cats/cats.module.ts index f3291c7d11e..14b21cd9f14 100644 --- a/sample/11-swagger/src/cats/cats.module.ts +++ b/sample/11-swagger/src/cats/cats.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; @Module({ controllers: [CatsController], diff --git a/sample/11-swagger/src/cats/cats.service.spec.ts b/sample/11-swagger/src/cats/cats.service.spec.ts new file mode 100644 index 00000000000..8e091e0775e --- /dev/null +++ b/sample/11-swagger/src/cats/cats.service.spec.ts @@ -0,0 +1,43 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CreateCatDto } from './dto/create-cat.dto'; +import { CatsService } from './cats.service'; + +describe('CatsService', () => { + let service: CatsService; + + const createCatDto: CreateCatDto = { + name: 'Kitty', + age: 2, + breed: 'Maine Coon', + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [CatsService], + }).compile(); + + service = module.get(CatsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + describe('create', () => { + it('should add a cat and return it', () => { + const result = service.create(createCatDto); + expect(result).toEqual(createCatDto); + }); + }); + + describe('findOne', () => { + it('should return undefined when no cats exist', () => { + expect(service.findOne(0)).toBeUndefined(); + }); + + it('should return the cat at the given index after creation', () => { + service.create(createCatDto); + expect(service.findOne(0)).toEqual(createCatDto); + }); + }); +}); diff --git a/sample/11-swagger/src/cats/cats.service.ts b/sample/11-swagger/src/cats/cats.service.ts index 3a7658d36df..688783ec908 100644 --- a/sample/11-swagger/src/cats/cats.service.ts +++ b/sample/11-swagger/src/cats/cats.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './entities/cat.entity'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './entities/cat.entity.js'; @Injectable() export class CatsService { diff --git a/sample/11-swagger/src/main.ts b/sample/11-swagger/src/main.ts index 82d9b5cfcc5..4b20e34f90c 100644 --- a/sample/11-swagger/src/main.ts +++ b/sample/11-swagger/src/main.ts @@ -1,6 +1,6 @@ import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -18,4 +18,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/11-swagger/tsconfig.json b/sample/11-swagger/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/11-swagger/tsconfig.json +++ b/sample/11-swagger/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/11-swagger/vitest.config.e2e.mts b/sample/11-swagger/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/11-swagger/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/11-swagger/vitest.config.mts b/sample/11-swagger/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/11-swagger/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/12-graphql-schema-first/e2e/cats/cats.e2e-spec.ts b/sample/12-graphql-schema-first/e2e/cats/cats.e2e-spec.ts index a1e446a8360..8b3f3e5c5bb 100644 --- a/sample/12-graphql-schema-first/e2e/cats/cats.e2e-spec.ts +++ b/sample/12-graphql-schema-first/e2e/cats/cats.e2e-spec.ts @@ -1,9 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import * as request from 'supertest'; +import request from 'supertest'; -import { Cat } from '../../src/graphql.schema'; -import { AppModule } from '../../src/app.module'; +import { Cat } from '../../src/graphql.schema.js'; +import { AppModule } from '../../src/app.module.js'; describe('Cats Resolver (e2e)', () => { let app: INestApplication; diff --git a/sample/12-graphql-schema-first/e2e/jest-e2e.json b/sample/12-graphql-schema-first/e2e/jest-e2e.json deleted file mode 100644 index 5be9543ccbb..00000000000 --- a/sample/12-graphql-schema-first/e2e/jest-e2e.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "moduleFileExtensions": ["ts", "tsx", "js", "json"], - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", - "collectCoverageFrom": [ - "src/**/*.{js,jsx,tsx,ts}", - "!**/node_modules/**", - "!**/vendor/**" - ], - "coverageReporters": ["json", "lcov"] -} diff --git a/sample/12-graphql-schema-first/eslint.config.mjs b/sample/12-graphql-schema-first/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/12-graphql-schema-first/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/12-graphql-schema-first/package.json b/sample/12-graphql-schema-first/package.json index 6473d00a050..0dbc8af5c55 100644 --- a/sample/12-graphql-schema-first/package.json +++ b/sample/12-graphql-schema-first/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./e2e/jest-e2e.json" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/server": "5.4.0", @@ -35,58 +35,21 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-morph": "27.0.2", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s", - "!main.(t|j)s", - "!**/*.module.(t|j)s", - "!**/*.dto.(t|j)s", - "!**/*.entity.(t|j)s", - "!**/*.guard.(t|j)s", - "!**/*.response.(t|j)s", - "!**/*.strategy.(t|j)s", - "!**/*.args.(t|j)s", - "!**/*.types.(t|j)s", - "!**/*.directive.(t|j)s", - "!**/*.plugin.(t|j)s", - "!**/*.scalar.(t|j)s", - "!**/*.schema.(t|j)s", - "!**/node_modules/**" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/12-graphql-schema-first/src/app.module.ts b/sample/12-graphql-schema-first/src/app.module.ts index c6833d0f695..b034dd99b36 100644 --- a/sample/12-graphql-schema-first/src/app.module.ts +++ b/sample/12-graphql-schema-first/src/app.module.ts @@ -1,8 +1,8 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; -import { CatsModule } from './cats/cats.module'; -import { upperDirectiveTransformer } from './common/directives/upper-case.directive'; +import { CatsModule } from './cats/cats.module.js'; +import { upperDirectiveTransformer } from './common/directives/upper-case.directive.js'; @Module({ imports: [ diff --git a/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.spec.ts b/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.spec.ts index d5878086edd..16b7a82b599 100644 --- a/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.spec.ts +++ b/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Cat, Owner } from '../graphql.schema'; -import { OwnersService } from '../owners/owners.service'; -import { CatOwnerResolver } from './cat-owner.resolver'; +import { Cat, Owner } from '../graphql.schema.js'; +import { OwnersService } from '../owners/owners.service.js'; +import { CatOwnerResolver } from './cat-owner.resolver.js'; describe('CatOwnerResolver', () => { let resolver: CatOwnerResolver; @@ -14,7 +14,7 @@ describe('CatOwnerResolver', () => { { provide: OwnersService, useValue: { - findOneById: jest.fn(), + findOneById: vi.fn(), }, }, ], @@ -32,7 +32,7 @@ describe('CatOwnerResolver', () => { const cat: Cat & { ownerId: number } = { id: 1, ownerId: 101 }; const owner: Owner = { id: 101, name: 'Kambale' }; - jest.spyOn(ownersService, 'findOneById').mockImplementation(() => owner); + vi.spyOn(ownersService, 'findOneById').mockImplementation(() => owner); const resolvedOwner = await resolver.owner(cat); diff --git a/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.ts b/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.ts index 65bb9adf174..902ec46f316 100644 --- a/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.ts +++ b/sample/12-graphql-schema-first/src/cats/cat-owner.resolver.ts @@ -1,13 +1,15 @@ import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { Cat, Owner } from '../graphql.schema'; -import { OwnersService } from '../owners/owners.service'; +import { Cat, Owner } from '../graphql.schema.js'; +import { OwnersService } from '../owners/owners.service.js'; @Resolver('Cat') export class CatOwnerResolver { constructor(private readonly ownersService: OwnersService) {} @ResolveField() - async owner(@Parent() cat: Cat & { ownerId: number }): Promise { + async owner( + @Parent() cat: Cat & { ownerId: number }, + ): Promise { return this.ownersService.findOneById(cat.ownerId); } } diff --git a/sample/12-graphql-schema-first/src/cats/cats.module.ts b/sample/12-graphql-schema-first/src/cats/cats.module.ts index 54874fae005..4f9e6bd9b96 100644 --- a/sample/12-graphql-schema-first/src/cats/cats.module.ts +++ b/sample/12-graphql-schema-first/src/cats/cats.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; -import { OwnersModule } from '../owners/owners.module'; -import { CatOwnerResolver } from './cat-owner.resolver'; -import { CatsResolver } from './cats.resolver'; -import { CatsService } from './cats.service'; +import { OwnersModule } from '../owners/owners.module.js'; +import { CatOwnerResolver } from './cat-owner.resolver.js'; +import { CatsResolver } from './cats.resolver.js'; +import { CatsService } from './cats.service.js'; @Module({ imports: [OwnersModule], diff --git a/sample/12-graphql-schema-first/src/cats/cats.resolver.spec.ts b/sample/12-graphql-schema-first/src/cats/cats.resolver.spec.ts index 717fafd6fdf..5e1f167af51 100644 --- a/sample/12-graphql-schema-first/src/cats/cats.resolver.spec.ts +++ b/sample/12-graphql-schema-first/src/cats/cats.resolver.spec.ts @@ -1,10 +1,10 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Cat } from '../graphql.schema'; -import { CatsResolver } from './cats.resolver'; -import { CatsService } from './cats.service'; +import { Cat } from '../graphql.schema.js'; +import { CatsResolver } from './cats.resolver.js'; +import { CatsService } from './cats.service.js'; -import { MINIMUM_AGE, MINIMUM_AGE_ERROR } from './dto/create-cat.dto'; +import { MINIMUM_AGE, MINIMUM_AGE_ERROR } from './dto/create-cat.dto.js'; describe('CatsResolver', () => { let resolver: CatsResolver; @@ -17,11 +17,11 @@ describe('CatsResolver', () => { { provide: CatsService, useValue: { - create: jest + create: vi .fn() .mockImplementation((cat: Cat) => ({ id: 1, ...cat })), - findAll: jest.fn().mockReturnValue([cat]), - findOneById: jest + findAll: vi.fn().mockReturnValue([cat]), + findOneById: vi .fn() .mockImplementation((id: number) => ({ ...cat, id })), }, diff --git a/sample/12-graphql-schema-first/src/cats/cats.resolver.ts b/sample/12-graphql-schema-first/src/cats/cats.resolver.ts index 93a0639b0bb..b561005561a 100644 --- a/sample/12-graphql-schema-first/src/cats/cats.resolver.ts +++ b/sample/12-graphql-schema-first/src/cats/cats.resolver.ts @@ -1,10 +1,10 @@ import { ParseIntPipe, UseGuards } from '@nestjs/common'; import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql'; import { PubSub } from 'graphql-subscriptions'; -import { Cat } from '../graphql.schema'; -import { CatsGuard } from './cats.guard'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; +import { Cat } from '../graphql.schema.js'; +import { CatsGuard } from './cats.guard.js'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; const pubSub = new PubSub(); @@ -22,7 +22,7 @@ export class CatsResolver { async findOneById( @Args('id', ParseIntPipe) id: number, - ): Promise { + ): Promise { return this.catsService.findOneById(id); } diff --git a/sample/12-graphql-schema-first/src/cats/cats.service.spec.ts b/sample/12-graphql-schema-first/src/cats/cats.service.spec.ts index 90e26f5bad5..57eb664ee6c 100644 --- a/sample/12-graphql-schema-first/src/cats/cats.service.spec.ts +++ b/sample/12-graphql-schema-first/src/cats/cats.service.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { CatsService } from './cats.service'; -import { Cat } from 'src/graphql.schema'; +import { CatsService } from './cats.service.js'; +import { Cat } from '../graphql.schema.js'; describe('CatsService', () => { let service: CatsService; diff --git a/sample/12-graphql-schema-first/src/cats/cats.service.ts b/sample/12-graphql-schema-first/src/cats/cats.service.ts index 58cbd6dd7c1..6d9e82fcba0 100644 --- a/sample/12-graphql-schema-first/src/cats/cats.service.ts +++ b/sample/12-graphql-schema-first/src/cats/cats.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Cat } from '../graphql.schema'; +import { Cat } from '../graphql.schema.js'; @Injectable() export class CatsService { @@ -17,7 +17,7 @@ export class CatsService { return this.cats; } - findOneById(id: number): Cat { + findOneById(id: number): Cat | undefined { return this.cats.find(cat => cat.id === id); } } diff --git a/sample/12-graphql-schema-first/src/cats/dto/create-cat.dto.ts b/sample/12-graphql-schema-first/src/cats/dto/create-cat.dto.ts index cfca24ae4a3..de42f41b048 100644 --- a/sample/12-graphql-schema-first/src/cats/dto/create-cat.dto.ts +++ b/sample/12-graphql-schema-first/src/cats/dto/create-cat.dto.ts @@ -1,10 +1,10 @@ import { Min } from 'class-validator'; -import { CreateCatInput } from '../../graphql.schema'; +import { CreateCatInput } from '../../graphql.schema.js'; export const MINIMUM_AGE = 1; export const MINIMUM_AGE_ERROR = `Age must be greater than ${MINIMUM_AGE}`; export class CreateCatDto extends CreateCatInput { @Min(MINIMUM_AGE, { message: MINIMUM_AGE_ERROR }) - age: number; + declare age: number; } diff --git a/sample/12-graphql-schema-first/src/common/scalars/date.scalar.ts b/sample/12-graphql-schema-first/src/common/scalars/date.scalar.ts index 1139c0264cc..2a11a632424 100644 --- a/sample/12-graphql-schema-first/src/common/scalars/date.scalar.ts +++ b/sample/12-graphql-schema-first/src/common/scalars/date.scalar.ts @@ -5,18 +5,18 @@ import { Kind } from 'graphql'; export class DateScalar implements CustomScalar { description = 'Date custom scalar type'; - parseValue(value: number): Date { - return new Date(value); // value from the client + parseValue(value: unknown): Date { + return new Date(value as number); // value from the client } - serialize(value: Date): number { - return value.getTime(); // value sent to the client + serialize(value: unknown): number { + return (value as Date).getTime(); // value sent to the client } parseLiteral(ast: any): Date { if (ast.kind === Kind.INT) { return new Date(ast.value); } - return null; + return null!; } } diff --git a/sample/12-graphql-schema-first/src/main.ts b/sample/12-graphql-schema-first/src/main.ts index ac2486c640c..261ee54ad60 100644 --- a/sample/12-graphql-schema-first/src/main.ts +++ b/sample/12-graphql-schema-first/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/12-graphql-schema-first/src/owners/owners.module.ts b/sample/12-graphql-schema-first/src/owners/owners.module.ts index 072a3764974..2aab73351a6 100644 --- a/sample/12-graphql-schema-first/src/owners/owners.module.ts +++ b/sample/12-graphql-schema-first/src/owners/owners.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { OwnersService } from './owners.service'; +import { OwnersService } from './owners.service.js'; @Module({ providers: [OwnersService], diff --git a/sample/12-graphql-schema-first/src/owners/owners.service.spec.ts b/sample/12-graphql-schema-first/src/owners/owners.service.spec.ts index 087c0aca434..ffc4603a0a0 100644 --- a/sample/12-graphql-schema-first/src/owners/owners.service.spec.ts +++ b/sample/12-graphql-schema-first/src/owners/owners.service.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Owner } from '../graphql.schema'; -import { OwnersService } from './owners.service'; +import { Owner } from '../graphql.schema.js'; +import { OwnersService } from './owners.service.js'; describe('OwnersService', () => { let service: OwnersService; diff --git a/sample/12-graphql-schema-first/src/owners/owners.service.ts b/sample/12-graphql-schema-first/src/owners/owners.service.ts index e207642e892..4c189514c9f 100644 --- a/sample/12-graphql-schema-first/src/owners/owners.service.ts +++ b/sample/12-graphql-schema-first/src/owners/owners.service.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common'; -import { Owner } from '../graphql.schema'; +import { Owner } from '../graphql.schema.js'; @Injectable() export class OwnersService { private readonly owners: Owner[] = [{ id: 1, name: 'Jon', age: 5 }]; - findOneById(id: number): Owner { + findOneById(id: number): Owner | undefined { return this.owners.find(owner => owner.id === id); } } diff --git a/sample/12-graphql-schema-first/tsconfig.json b/sample/12-graphql-schema-first/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/12-graphql-schema-first/tsconfig.json +++ b/sample/12-graphql-schema-first/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/12-graphql-schema-first/vitest.config.e2e.mts b/sample/12-graphql-schema-first/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/12-graphql-schema-first/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/12-graphql-schema-first/vitest.config.mts b/sample/12-graphql-schema-first/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/12-graphql-schema-first/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/13-mongo-typeorm/e2e/photo/photo.e2e-spec.ts b/sample/13-mongo-typeorm/e2e/photo/photo.e2e-spec.ts new file mode 100644 index 00000000000..0aefeee3227 --- /dev/null +++ b/sample/13-mongo-typeorm/e2e/photo/photo.e2e-spec.ts @@ -0,0 +1,87 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { MongoMemoryServer } from 'mongodb-memory-server'; +import request from 'supertest'; +import { PhotoModule } from '../../src/photo/photo.module.js'; +import { Photo } from '../../src/photo/photo.entity.js'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { MongoRepository } from 'typeorm'; + +describe('PhotoController (e2e)', () => { + let app: INestApplication; + let mongoServer: MongoMemoryServer; + let photoRepository: MongoRepository; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [ + TypeOrmModule.forRoot({ + type: 'mongodb', + url: mongoUri, + entities: [Photo], + synchronize: true, + }), + PhotoModule, + ], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + + photoRepository = moduleFixture.get>( + getRepositoryToken(Photo), + ); + }, 30000); + + afterEach(async () => { + await photoRepository.clear(); + }); + + afterAll(async () => { + await app.close(); + await mongoServer.stop(); + }); + + describe('GET /photo', () => { + it('should return an empty array when no photos exist', async () => { + const response = await request(app.getHttpServer()) + .get('/photo') + .expect(200); + + expect(response.body).toEqual([]); + }); + + it('should return all photos', async () => { + await photoRepository.save([ + { + name: 'Photo #1', + description: 'Description #1', + filename: 'photo1.jpg', + isPublished: true, + }, + { + name: 'Photo #2', + description: 'Description #2', + filename: 'photo2.jpg', + isPublished: false, + }, + ]); + + const response = await request(app.getHttpServer()) + .get('/photo') + .expect(200); + + expect(response.body).toHaveLength(2); + expect(response.body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'Photo #1', filename: 'photo1.jpg' }), + expect.objectContaining({ name: 'Photo #2', filename: 'photo2.jpg' }), + ]), + ); + }); + }); +}); diff --git a/sample/13-mongo-typeorm/eslint.config.mjs b/sample/13-mongo-typeorm/eslint.config.mjs deleted file mode 100644 index 86bd271de77..00000000000 --- a/sample/13-mongo-typeorm/eslint.config.mjs +++ /dev/null @@ -1,41 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/13-mongo-typeorm/package.json b/sample/13-mongo-typeorm/package.json index 144d08063fd..fe3acdba0df 100644 --- a/sample/13-mongo-typeorm/package.json +++ b/sample/13-mongo-typeorm/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -30,43 +30,21 @@ "typeorm": "0.3.28" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "mongodb-memory-server": "^11.0.1", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/13-mongo-typeorm/src/app.module.ts b/sample/13-mongo-typeorm/src/app.module.ts index 1406b773de4..537ba614614 100644 --- a/sample/13-mongo-typeorm/src/app.module.ts +++ b/sample/13-mongo-typeorm/src/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { Photo } from './photo/photo.entity'; -import { PhotoModule } from './photo/photo.module'; +import { Photo } from './photo/photo.entity.js'; +import { PhotoModule } from './photo/photo.module.js'; @Module({ imports: [ diff --git a/sample/13-mongo-typeorm/src/main.ts b/sample/13-mongo-typeorm/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/13-mongo-typeorm/src/main.ts +++ b/sample/13-mongo-typeorm/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/13-mongo-typeorm/src/photo/photo.controller.spec.ts b/sample/13-mongo-typeorm/src/photo/photo.controller.spec.ts index 6d830d2630d..f1816607343 100644 --- a/sample/13-mongo-typeorm/src/photo/photo.controller.spec.ts +++ b/sample/13-mongo-typeorm/src/photo/photo.controller.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { PhotoController } from './photo.controller'; -import { PhotoService } from './photo.service'; +import { PhotoController } from './photo.controller.js'; +import { PhotoService } from './photo.service.js'; describe('Photo Controller', () => { let controller: PhotoController; @@ -12,7 +12,7 @@ describe('Photo Controller', () => { { provide: PhotoService, useValue: { - findAll: jest.fn().mockResolvedValue([ + findAll: vi.fn().mockResolvedValue([ { name: 'Photo #1', description: 'Description #1', diff --git a/sample/13-mongo-typeorm/src/photo/photo.controller.ts b/sample/13-mongo-typeorm/src/photo/photo.controller.ts index b3d3b2eb063..f6f9adfd4c7 100644 --- a/sample/13-mongo-typeorm/src/photo/photo.controller.ts +++ b/sample/13-mongo-typeorm/src/photo/photo.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get } from '@nestjs/common'; -import { PhotoService } from './photo.service'; -import { Photo } from './photo.entity'; +import { PhotoService } from './photo.service.js'; +import { Photo } from './photo.entity.js'; @Controller('photo') export class PhotoController { diff --git a/sample/13-mongo-typeorm/src/photo/photo.module.ts b/sample/13-mongo-typeorm/src/photo/photo.module.ts index 72a3fa21688..7fecc3adc56 100644 --- a/sample/13-mongo-typeorm/src/photo/photo.module.ts +++ b/sample/13-mongo-typeorm/src/photo/photo.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { PhotoService } from './photo.service'; -import { PhotoController } from './photo.controller'; -import { Photo } from './photo.entity'; +import { PhotoService } from './photo.service.js'; +import { PhotoController } from './photo.controller.js'; +import { Photo } from './photo.entity.js'; @Module({ imports: [TypeOrmModule.forFeature([Photo])], diff --git a/sample/13-mongo-typeorm/src/photo/photo.service.spec.ts b/sample/13-mongo-typeorm/src/photo/photo.service.spec.ts index a47dbc0c6a2..408047b1096 100644 --- a/sample/13-mongo-typeorm/src/photo/photo.service.spec.ts +++ b/sample/13-mongo-typeorm/src/photo/photo.service.spec.ts @@ -1,8 +1,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { Photo } from './photo.entity'; -import { PhotoService } from './photo.service'; +import { Photo } from './photo.entity.js'; +import { PhotoService } from './photo.service.js'; describe('CatService', () => { let service: PhotoService; @@ -30,7 +30,7 @@ describe('CatService', () => { { provide: getRepositoryToken(Photo), useValue: { - find: jest.fn().mockResolvedValue(photosArray), + find: vi.fn().mockResolvedValue(photosArray), }, }, ], diff --git a/sample/13-mongo-typeorm/src/photo/photo.service.ts b/sample/13-mongo-typeorm/src/photo/photo.service.ts index 0bca3ddeeb0..99f24152d9d 100644 --- a/sample/13-mongo-typeorm/src/photo/photo.service.ts +++ b/sample/13-mongo-typeorm/src/photo/photo.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { MongoRepository } from 'typeorm'; -import { Photo } from './photo.entity'; +import { Photo } from './photo.entity.js'; @Injectable() export class PhotoService { diff --git a/sample/13-mongo-typeorm/tsconfig.json b/sample/13-mongo-typeorm/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/13-mongo-typeorm/tsconfig.json +++ b/sample/13-mongo-typeorm/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/13-mongo-typeorm/vitest.config.e2e.mts b/sample/13-mongo-typeorm/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/13-mongo-typeorm/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/13-mongo-typeorm/vitest.config.mts b/sample/13-mongo-typeorm/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/13-mongo-typeorm/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/14-mongoose-base/e2e/cats/cats.e2e-spec.ts b/sample/14-mongoose-base/e2e/cats/cats.e2e-spec.ts new file mode 100644 index 00000000000..5a3d489610d --- /dev/null +++ b/sample/14-mongoose-base/e2e/cats/cats.e2e-spec.ts @@ -0,0 +1,100 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { MongoMemoryServer } from 'mongodb-memory-server'; +import * as mongoose from 'mongoose'; +import request from 'supertest'; +import { CatsModule } from '../../src/cats/cats.module.js'; +import { DatabaseModule } from '../../src/database/database.module.js'; + +describe('CatsController (e2e)', () => { + let app: INestApplication; + let mongoServer: MongoMemoryServer; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [CatsModule], + }) + .overrideProvider('DATABASE_CONNECTION') + .useFactory({ + factory: async () => await mongoose.connect(mongoUri), + }) + .compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }, 30000); + + afterEach(async () => { + const collections = mongoose.connection.collections; + for (const key in collections) { + await collections[key].deleteMany({}); + } + }); + + afterAll(async () => { + await app.close(); + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + describe('POST /cats', () => { + it('should create a new cat and return it', async () => { + const createCatDto = { name: 'Whiskers', age: 3, breed: 'Persian' }; + + const response = await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + + expect(response.body).toMatchObject(createCatDto); + expect(response.body._id).toBeDefined(); + }); + + it('should persist the cat to the database', async () => { + const createCatDto = { name: 'Luna', age: 2, breed: 'Siamese' }; + + await request(app.getHttpServer()) + .post('/cats') + .send(createCatDto) + .expect(201); + + const catInDb = await mongoose.connection + .collection('cats') + .findOne({ name: 'Luna' }); + expect(catInDb).not.toBeNull(); + expect(catInDb?.breed).toBe('Siamese'); + }); + }); + + describe('GET /cats', () => { + it('should return an empty array when no cats exist', async () => { + const response = await request(app.getHttpServer()) + .get('/cats') + .expect(200); + + expect(response.body).toEqual([]); + }); + + it('should return all cats', async () => { + await mongoose.connection.collection('cats').insertMany([ + { name: 'Whiskers', age: 3, breed: 'Persian' }, + { name: 'Luna', age: 2, breed: 'Siamese' }, + ]); + + const response = await request(app.getHttpServer()) + .get('/cats') + .expect(200); + + expect(response.body).toHaveLength(2); + expect(response.body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'Whiskers', breed: 'Persian' }), + expect.objectContaining({ name: 'Luna', breed: 'Siamese' }), + ]), + ); + }); + }); +}); diff --git a/sample/14-mongoose-base/eslint.config.mjs b/sample/14-mongoose-base/eslint.config.mjs deleted file mode 100644 index 0c8a9ec0fc0..00000000000 --- a/sample/14-mongoose-base/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/require-await': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/14-mongoose-base/package.json b/sample/14-mongoose-base/package.json index 08a76773307..abf0b6ba902 100644 --- a/sample/14-mongoose-base/package.json +++ b/sample/14-mongoose-base/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -28,43 +28,21 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "mongodb-memory-server": "^11.0.1", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/14-mongoose-base/src/app.module.ts b/sample/14-mongoose-base/src/app.module.ts index 77390842912..ce4c4578fa5 100644 --- a/sample/14-mongoose-base/src/app.module.ts +++ b/sample/14-mongoose-base/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { CatsModule } from './cats/cats.module'; +import { CatsModule } from './cats/cats.module.js'; @Module({ imports: [CatsModule], diff --git a/sample/14-mongoose-base/src/cats/cats.controller.spec.ts b/sample/14-mongoose-base/src/cats/cats.controller.spec.ts index 25727b18b84..1d0f9db7edf 100644 --- a/sample/14-mongoose-base/src/cats/cats.controller.spec.ts +++ b/sample/14-mongoose-base/src/cats/cats.controller.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { CatsController } from './cats.controller'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { CatsService } from './cats.service.js'; describe('CatsController', () => { let controller: CatsController; @@ -14,7 +14,7 @@ describe('CatsController', () => { { provide: CatsService, useValue: { - findAll: jest.fn().mockResolvedValue([ + findAll: vi.fn().mockResolvedValue([ { name: 'Cat #1', breed: 'Bread #1', @@ -31,7 +31,7 @@ describe('CatsController', () => { age: 2, }, ]), - create: jest + create: vi .fn() .mockImplementation((createCatDto: CreateCatDto) => Promise.resolve({ _id: '1', ...createCatDto }), diff --git a/sample/14-mongoose-base/src/cats/cats.controller.ts b/sample/14-mongoose-base/src/cats/cats.controller.ts index e04b507a1e2..1c699c022ea 100644 --- a/sample/14-mongoose-base/src/cats/cats.controller.ts +++ b/sample/14-mongoose-base/src/cats/cats.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Post, Body } from '@nestjs/common'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { CatsService } from './cats.service'; -import { Cat } from './interfaces/cat.interface'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; @Controller('cats') export class CatsController { diff --git a/sample/14-mongoose-base/src/cats/cats.module.ts b/sample/14-mongoose-base/src/cats/cats.module.ts index 06f59c78436..512ce39c8e7 100644 --- a/sample/14-mongoose-base/src/cats/cats.module.ts +++ b/sample/14-mongoose-base/src/cats/cats.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; -import { catsProviders } from './cats.providers'; -import { DatabaseModule } from '../database/database.module'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { catsProviders } from './cats.providers.js'; +import { DatabaseModule } from '../database/database.module.js'; @Module({ imports: [DatabaseModule], diff --git a/sample/14-mongoose-base/src/cats/cats.providers.ts b/sample/14-mongoose-base/src/cats/cats.providers.ts index c737784cc85..ec8878392e6 100644 --- a/sample/14-mongoose-base/src/cats/cats.providers.ts +++ b/sample/14-mongoose-base/src/cats/cats.providers.ts @@ -1,5 +1,5 @@ import { Mongoose } from 'mongoose'; -import { CatSchema } from './schemas/cat.schema'; +import { CatSchema } from './schemas/cat.schema.js'; export const catsProviders = [ { diff --git a/sample/14-mongoose-base/src/cats/cats.service.spec.ts b/sample/14-mongoose-base/src/cats/cats.service.spec.ts index 5e8aba1351d..04aaaa17c56 100644 --- a/sample/14-mongoose-base/src/cats/cats.service.spec.ts +++ b/sample/14-mongoose-base/src/cats/cats.service.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Model } from 'mongoose'; -import { CatsService } from './cats.service'; -import { Cat } from './interfaces/cat.interface'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; const mockCat = { name: 'Cat #1', @@ -33,12 +33,12 @@ describe('CatService', () => { { provide: 'CAT_MODEL', useValue: { - new: jest.fn().mockResolvedValue(mockCat), - constructor: jest.fn().mockResolvedValue(mockCat), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - exec: jest.fn(), + new: vi.fn().mockResolvedValue(mockCat), + constructor: vi.fn().mockResolvedValue(mockCat), + find: vi.fn(), + create: vi.fn(), + save: vi.fn(), + exec: vi.fn(), }, }, ], @@ -53,15 +53,15 @@ describe('CatService', () => { }); it('should return all cats', async () => { - jest.spyOn(model, 'find').mockReturnValue({ - exec: jest.fn().mockResolvedValueOnce(catsArray), + vi.spyOn(model, 'find').mockReturnValue({ + exec: vi.fn().mockResolvedValueOnce(catsArray), } as any); const cats = await service.findAll(); expect(cats).toEqual(catsArray); }); it('should insert a new cat', async () => { - jest.spyOn(model, 'create').mockImplementationOnce(() => + vi.spyOn(model, 'create').mockImplementationOnce(() => Promise.resolve({ name: 'Cat #1', breed: 'Breed #1', diff --git a/sample/14-mongoose-base/src/cats/cats.service.ts b/sample/14-mongoose-base/src/cats/cats.service.ts index 1bbddc4da46..b4d423f8a59 100644 --- a/sample/14-mongoose-base/src/cats/cats.service.ts +++ b/sample/14-mongoose-base/src/cats/cats.service.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Model } from 'mongoose'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './interfaces/cat.interface'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable() export class CatsService { diff --git a/sample/14-mongoose-base/src/database/database.module.ts b/sample/14-mongoose-base/src/database/database.module.ts index 857bc80e713..f992fa7fa57 100644 --- a/sample/14-mongoose-base/src/database/database.module.ts +++ b/sample/14-mongoose-base/src/database/database.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { databaseProviders } from './database.providers'; +import { databaseProviders } from './database.providers.js'; @Module({ providers: [...databaseProviders], diff --git a/sample/14-mongoose-base/src/main.ts b/sample/14-mongoose-base/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/14-mongoose-base/src/main.ts +++ b/sample/14-mongoose-base/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/14-mongoose-base/tsconfig.json b/sample/14-mongoose-base/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/14-mongoose-base/tsconfig.json +++ b/sample/14-mongoose-base/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/14-mongoose-base/vitest.config.e2e.mts b/sample/14-mongoose-base/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/14-mongoose-base/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/14-mongoose-base/vitest.config.mts b/sample/14-mongoose-base/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/14-mongoose-base/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/15-mvc/e2e/app/app.e2e-spec.ts b/sample/15-mvc/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..17c5e5ad8e8 --- /dev/null +++ b/sample/15-mvc/e2e/app/app.e2e-spec.ts @@ -0,0 +1,50 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { NestExpressApplication } from '@nestjs/platform-express'; +import request from 'supertest'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; +import { AppModule } from '../../src/app.module.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +describe('AppController (e2e)', () => { + let app: NestExpressApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + app.setBaseViewsDir(join(__dirname, '..', '..', 'views')); + app.setViewEngine('hbs'); + + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return HTML content', async () => { + await request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Content-Type', /html/); + }); + + it('should render the template with the message variable', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.text).toContain('Hello world!'); + }); + + it('should return a valid HTML document', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.text).toContain(''); + expect(response.text).toContain('(AppModule); @@ -13,4 +16,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/15-mvc/tsconfig.json b/sample/15-mvc/tsconfig.json index d9c82ca9758..7826296e686 100644 --- a/sample/15-mvc/tsconfig.json +++ b/sample/15-mvc/tsconfig.json @@ -1,17 +1,25 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals", + "node" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/15-mvc/vitest.config.e2e.mts b/sample/15-mvc/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/15-mvc/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/15-mvc/vitest.config.mts b/sample/15-mvc/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/15-mvc/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/16-gateways-ws/e2e/events/events.e2e-spec.ts b/sample/16-gateways-ws/e2e/events/events.e2e-spec.ts new file mode 100644 index 00000000000..84f71449da3 --- /dev/null +++ b/sample/16-gateways-ws/e2e/events/events.e2e-spec.ts @@ -0,0 +1,60 @@ +import { INestApplication } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import { WsAdapter } from '@nestjs/platform-ws'; +import { WebSocket } from 'ws'; +import { AppModule } from '../../src/app.module.js'; + +describe('EventsGateway (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleRef.createNestApplication(); + app.useWebSocketAdapter(new WsAdapter(app)); + await app.listen(3000); + }, 30000); + + afterAll(async () => { + await app.close(); + }); + + describe('events', () => { + it('should receive 3 events', () => { + return new Promise((resolve, reject) => { + const ws = new WebSocket('ws://localhost:8080'); + const received: number[] = []; + + ws.on('open', () => { + ws.send(JSON.stringify({ event: 'events', data: {} })); + }); + + ws.on('message', (raw: Buffer) => { + const message = JSON.parse(raw.toString()); + if (message.event === 'events') { + received.push(message.data); + if (received.length === 3) { + try { + expect(received).toEqual([1, 2, 3]); + ws.close(); + resolve(); + } catch (err) { + ws.close(); + reject(err); + } + } + } + }); + + ws.on('error', reject); + + setTimeout(() => { + ws.close(); + reject(new Error('Timeout waiting for events')); + }, 5000); + }); + }); + }); +}); diff --git a/sample/16-gateways-ws/eslint.config.mjs b/sample/16-gateways-ws/eslint.config.mjs deleted file mode 100644 index 0c8a9ec0fc0..00000000000 --- a/sample/16-gateways-ws/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/require-await': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/16-gateways-ws/package.json b/sample/16-gateways-ws/package.json index 230852c5831..1d9e14dacb0 100644 --- a/sample/16-gateways-ws/package.json +++ b/sample/16-gateways-ws/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -32,26 +32,21 @@ "ws": "8.18.0" }, "devDependencies": { - "@types/ws": "8.5.13", - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "@types/ws": "8.5.13", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/16-gateways-ws/src/app.module.ts b/sample/16-gateways-ws/src/app.module.ts index a5d9e1125d8..dded5147a9e 100644 --- a/sample/16-gateways-ws/src/app.module.ts +++ b/sample/16-gateways-ws/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { EventsModule } from './events/events.module'; +import { EventsModule } from './events/events.module.js'; @Module({ imports: [EventsModule], diff --git a/sample/16-gateways-ws/src/events/events.gateway.ts b/sample/16-gateways-ws/src/events/events.gateway.ts index 1b6430cd32a..f6d788c7c98 100644 --- a/sample/16-gateways-ws/src/events/events.gateway.ts +++ b/sample/16-gateways-ws/src/events/events.gateway.ts @@ -6,7 +6,7 @@ import { } from '@nestjs/websockets'; import { from, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { Server } from 'ws'; +import { WebSocketServer as Server } from 'ws'; @WebSocketGateway(8080) export class EventsGateway { diff --git a/sample/16-gateways-ws/src/events/events.module.ts b/sample/16-gateways-ws/src/events/events.module.ts index 2b2d1cbb36d..bf4644469bf 100644 --- a/sample/16-gateways-ws/src/events/events.module.ts +++ b/sample/16-gateways-ws/src/events/events.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { EventsGateway } from './events.gateway'; +import { EventsGateway } from './events.gateway.js'; @Module({ providers: [EventsGateway], diff --git a/sample/16-gateways-ws/src/main.ts b/sample/16-gateways-ws/src/main.ts index c6ebe8a63fe..645b7072383 100644 --- a/sample/16-gateways-ws/src/main.ts +++ b/sample/16-gateways-ws/src/main.ts @@ -1,6 +1,6 @@ import { NestFactory } from '@nestjs/core'; import { WsAdapter } from '@nestjs/platform-ws'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/16-gateways-ws/tsconfig.json b/sample/16-gateways-ws/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/16-gateways-ws/tsconfig.json +++ b/sample/16-gateways-ws/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/16-gateways-ws/vitest.config.e2e.mts b/sample/16-gateways-ws/vitest.config.e2e.mts new file mode 100644 index 00000000000..0f98631f448 --- /dev/null +++ b/sample/16-gateways-ws/vitest.config.e2e.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + hookTimeout: 30000, + } +}); diff --git a/sample/16-gateways-ws/vitest.config.mts b/sample/16-gateways-ws/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/16-gateways-ws/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/17-mvc-fastify/e2e/app/app.e2e-spec.ts b/sample/17-mvc-fastify/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..5eefcda5485 --- /dev/null +++ b/sample/17-mvc-fastify/e2e/app/app.e2e-spec.ts @@ -0,0 +1,61 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import request from 'supertest'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; +import handlebars from 'handlebars'; +import { AppModule } from '../../src/app.module.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +describe('AppController (e2e)', () => { + let app: NestFastifyApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + app.setViewEngine({ + engine: { + handlebars, + }, + templates: join(__dirname, '..', '..', 'views'), + }); + + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return HTML content', async () => { + await request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Content-Type', /html/); + }); + + it('should render the template with the message variable', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.text).toContain('Hello world!'); + }); + + it('should return a valid HTML document', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.text).toContain(''); + expect(response.text).toContain('( @@ -17,7 +21,7 @@ async function bootstrap() { }); app.setViewEngine({ engine: { - handlebars: require('handlebars'), + handlebars, }, templates: join(__dirname, '..', 'views'), }); @@ -25,4 +29,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/17-mvc-fastify/tsconfig.json b/sample/17-mvc-fastify/tsconfig.json index d9c82ca9758..7826296e686 100644 --- a/sample/17-mvc-fastify/tsconfig.json +++ b/sample/17-mvc-fastify/tsconfig.json @@ -1,17 +1,25 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals", + "node" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/17-mvc-fastify/vitest.config.e2e.mts b/sample/17-mvc-fastify/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/17-mvc-fastify/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/17-mvc-fastify/vitest.config.mts b/sample/17-mvc-fastify/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/17-mvc-fastify/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/18-context/e2e/app/app.e2e-spec.ts b/sample/18-context/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..21508da02c2 --- /dev/null +++ b/sample/18-context/e2e/app/app.e2e-spec.ts @@ -0,0 +1,36 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule, dynamicModule } from '../../src/app.module.js'; +import { AppService } from '../../src/app.service.js'; + +describe('Application Context (e2e)', () => { + let module: TestingModule; + + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + }); + + afterAll(async () => { + await module.close(); + }); + + describe('AppService', () => { + it('should resolve AppService from the container', () => { + const appService = module.get(AppService); + expect(appService).toBeDefined(); + }); + + it('should return the hello message', () => { + const appService = module.get(AppService); + expect(appService.getHello()).toBe('Hello world!'); + }); + }); + + describe('MyDynamicModule', () => { + it('should resolve MyDynamicProvider with the registered value', () => { + const value = module.select(dynamicModule).get('MyDynamicProvider'); + expect(value).toBe('foobar'); + }); + }); +}); diff --git a/sample/18-context/eslint.config.mjs b/sample/18-context/eslint.config.mjs deleted file mode 100644 index c3eec95e2f6..00000000000 --- a/sample/18-context/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-require-imports': 'off' - }, - }, -); \ No newline at end of file diff --git a/sample/18-context/package.json b/sample/18-context/package.json index 0f54a56fcfc..ca446ca043a 100644 --- a/sample/18-context/package.json +++ b/sample/18-context/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -26,23 +26,18 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/18-context/src/app.module.ts b/sample/18-context/src/app.module.ts index 0f9c3fb3a5f..5b6cf71baf9 100644 --- a/sample/18-context/src/app.module.ts +++ b/sample/18-context/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AppService } from './app.service'; -import { MyDynamicModule } from './my-dynamic.module'; +import { AppService } from './app.service.js'; +import { MyDynamicModule } from './my-dynamic.module.js'; export const dynamicModule = MyDynamicModule.register('foobar'); diff --git a/sample/18-context/src/main.ts b/sample/18-context/src/main.ts index a6e8f13a86e..8c4ab8ab914 100644 --- a/sample/18-context/src/main.ts +++ b/sample/18-context/src/main.ts @@ -1,6 +1,6 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule, dynamicModule } from './app.module'; -import { AppService } from './app.service'; +import { AppModule, dynamicModule } from './app.module.js'; +import { AppService } from './app.service.js'; async function bootstrap() { const app = await NestFactory.createApplicationContext(AppModule); @@ -16,4 +16,4 @@ async function bootstrap() { return app.close(); } -bootstrap(); +await bootstrap(); diff --git a/sample/18-context/tsconfig.json b/sample/18-context/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/18-context/tsconfig.json +++ b/sample/18-context/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/18-context/vitest.config.e2e.mts b/sample/18-context/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/18-context/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/18-context/vitest.config.mts b/sample/18-context/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/18-context/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/19-auth-jwt/e2e/app/app.e2e-spec.ts b/sample/19-auth-jwt/e2e/app/app.e2e-spec.ts index b92a425e187..9f2df7d3de6 100644 --- a/sample/19-auth-jwt/e2e/app/app.e2e-spec.ts +++ b/sample/19-auth-jwt/e2e/app/app.e2e-spec.ts @@ -1,7 +1,7 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from '../../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; describe('E2E JWT Sample', () => { let app: INestApplication; diff --git a/sample/19-auth-jwt/e2e/jest-e2e.json b/sample/19-auth-jwt/e2e/jest-e2e.json deleted file mode 100644 index 8b41ad1d57f..00000000000 --- a/sample/19-auth-jwt/e2e/jest-e2e.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ], - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", - "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], - "coverageReporters": ["json", "lcov"] -} diff --git a/sample/19-auth-jwt/eslint.config.mjs b/sample/19-auth-jwt/eslint.config.mjs deleted file mode 100644 index d6b5176aff4..00000000000 --- a/sample/19-auth-jwt/eslint.config.mjs +++ /dev/null @@ -1,44 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-require-imports': 'off', - '@typescript-eslint/no-redundant-type-constituents': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/19-auth-jwt/package.json b/sample/19-auth-jwt/package.json index 72b82350e1f..9cc70f374cc 100644 --- a/sample/19-auth-jwt/package.json +++ b/sample/19-auth-jwt/package.json @@ -12,12 +12,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./e2e/jest-e2e.json" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -30,40 +30,20 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/19-auth-jwt/src/app.module.ts b/sample/19-auth-jwt/src/app.module.ts index 5424fcff134..5759498dd18 100644 --- a/sample/19-auth-jwt/src/app.module.ts +++ b/sample/19-auth-jwt/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AuthModule } from './auth/auth.module'; -import { UsersModule } from './users/users.module'; +import { AuthModule } from './auth/auth.module.js'; +import { UsersModule } from './users/users.module.js'; @Module({ imports: [AuthModule, UsersModule], diff --git a/sample/19-auth-jwt/src/auth/auth.controller.ts b/sample/19-auth-jwt/src/auth/auth.controller.ts index 8e7b51642c6..3fd67e2980f 100644 --- a/sample/19-auth-jwt/src/auth/auth.controller.ts +++ b/sample/19-auth-jwt/src/auth/auth.controller.ts @@ -7,8 +7,8 @@ import { Post, Request, } from '@nestjs/common'; -import { AuthService } from './auth.service'; -import { Public } from './decorators/public.decorator'; +import { AuthService } from './auth.service.js'; +import { Public } from './decorators/public.decorator.js'; @Controller('auth') export class AuthController { @@ -22,7 +22,7 @@ export class AuthController { } @Get('profile') - getProfile(@Request() req) { + getProfile(@Request() req: any) { return req.user; } } diff --git a/sample/19-auth-jwt/src/auth/auth.guard.ts b/sample/19-auth-jwt/src/auth/auth.guard.ts index 8ae499c1b18..b72bc63cafc 100644 --- a/sample/19-auth-jwt/src/auth/auth.guard.ts +++ b/sample/19-auth-jwt/src/auth/auth.guard.ts @@ -7,8 +7,8 @@ import { import { Reflector } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; import { Request } from 'express'; -import { jwtConstants } from './constants'; -import { IS_PUBLIC_KEY } from './decorators/public.decorator'; +import { jwtConstants } from './constants.js'; +import { IS_PUBLIC_KEY } from './decorators/public.decorator.js'; @Injectable() export class AuthGuard implements CanActivate { diff --git a/sample/19-auth-jwt/src/auth/auth.module.ts b/sample/19-auth-jwt/src/auth/auth.module.ts index 9361229eb5e..b0409495c71 100644 --- a/sample/19-auth-jwt/src/auth/auth.module.ts +++ b/sample/19-auth-jwt/src/auth/auth.module.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; import { JwtModule } from '@nestjs/jwt'; -import { UsersModule } from '../users/users.module'; -import { AuthController } from './auth.controller'; -import { AuthGuard } from './auth.guard'; -import { AuthService } from './auth.service'; -import { jwtConstants } from './constants'; +import { UsersModule } from '../users/users.module.js'; +import { AuthController } from './auth.controller.js'; +import { AuthGuard } from './auth.guard.js'; +import { AuthService } from './auth.service.js'; +import { jwtConstants } from './constants.js'; @Module({ imports: [ diff --git a/sample/19-auth-jwt/src/auth/auth.service.ts b/sample/19-auth-jwt/src/auth/auth.service.ts index f97c917d788..791b43d1792 100644 --- a/sample/19-auth-jwt/src/auth/auth.service.ts +++ b/sample/19-auth-jwt/src/auth/auth.service.ts @@ -1,6 +1,6 @@ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; -import { UsersService } from '../users/users.service'; +import { UsersService } from '../users/users.service.js'; @Injectable() export class AuthService { diff --git a/sample/19-auth-jwt/src/main.ts b/sample/19-auth-jwt/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/19-auth-jwt/src/main.ts +++ b/sample/19-auth-jwt/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/19-auth-jwt/src/users/users.module.ts b/sample/19-auth-jwt/src/users/users.module.ts index 8fa904f17db..1b08105e4e4 100644 --- a/sample/19-auth-jwt/src/users/users.module.ts +++ b/sample/19-auth-jwt/src/users/users.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Module({ providers: [UsersService], diff --git a/sample/19-auth-jwt/src/users/users.service.spec.ts b/sample/19-auth-jwt/src/users/users.service.spec.ts index 62815ba6412..35db466e8a3 100644 --- a/sample/19-auth-jwt/src/users/users.service.spec.ts +++ b/sample/19-auth-jwt/src/users/users.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; describe('UsersService', () => { let service: UsersService; diff --git a/sample/19-auth-jwt/tsconfig.json b/sample/19-auth-jwt/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/19-auth-jwt/tsconfig.json +++ b/sample/19-auth-jwt/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/19-auth-jwt/vitest.config.e2e.mts b/sample/19-auth-jwt/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/19-auth-jwt/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/19-auth-jwt/vitest.config.mts b/sample/19-auth-jwt/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/19-auth-jwt/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/20-cache/e2e/app/app.e2e-spec.ts b/sample/20-cache/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..114aa9d075e --- /dev/null +++ b/sample/20-cache/e2e/app/app.e2e-spec.ts @@ -0,0 +1,47 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return an array of items and cache the response', async () => { + const start1 = Date.now(); + const first = await request(app.getHttpServer()).get('/').expect(200); + const duration1 = Date.now() - start1; + + expect(first.body).toEqual([{ id: 1, name: 'Nest' }]); + + const start2 = Date.now(); + const second = await request(app.getHttpServer()).get('/').expect(200); + const duration2 = Date.now() - start2; + + expect(second.body).toEqual(first.body); + expect(duration2).toBeLessThan(duration1); + }, 15000); + + it('should consistently return the same cached data', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.body).toEqual([{ id: 1, name: 'Nest' }]); + expect(response.body).toHaveLength(1); + expect(response.body[0]).toHaveProperty('id'); + expect(response.body[0]).toHaveProperty('name'); + }); + }); +}); diff --git a/sample/20-cache/eslint.config.mjs b/sample/20-cache/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/20-cache/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/20-cache/package.json b/sample/20-cache/package.json index d3f94638e09..e705f46ce35 100644 --- a/sample/20-cache/package.json +++ b/sample/20-cache/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/cache-manager": "3.1.0", @@ -30,28 +30,22 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "rimraf": "6.1.2", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/20-cache/src/app.controller.spec.ts b/sample/20-cache/src/app.controller.spec.ts new file mode 100644 index 00000000000..ed09ad7bf3f --- /dev/null +++ b/sample/20-cache/src/app.controller.spec.ts @@ -0,0 +1,40 @@ +import { vi } from 'vitest'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; + +describe('AppController', () => { + let controller: AppController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [ + { + provide: CACHE_MANAGER, + useValue: { get: vi.fn(), set: vi.fn() }, + }, + ], + }).compile(); + + controller = module.get(AppController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + describe('findAll', () => { + it('should return an array of items', async () => { + vi.useFakeTimers(); + + const promise = controller.findAll(); + vi.runAllTimers(); + const result = await promise; + + expect(result).toEqual([{ id: 1, name: 'Nest' }]); + + vi.useRealTimers(); + }); + }); +}); diff --git a/sample/20-cache/src/app.module.ts b/sample/20-cache/src/app.module.ts index 310d05226c3..9fc7342d454 100644 --- a/sample/20-cache/src/app.module.ts +++ b/sample/20-cache/src/app.module.ts @@ -1,6 +1,6 @@ import { CacheModule } from '@nestjs/cache-manager'; import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; @Module({ imports: [CacheModule.register()], diff --git a/sample/20-cache/src/common/http-cache.interceptor.spec.ts b/sample/20-cache/src/common/http-cache.interceptor.spec.ts new file mode 100644 index 00000000000..f62be7f7244 --- /dev/null +++ b/sample/20-cache/src/common/http-cache.interceptor.spec.ts @@ -0,0 +1,97 @@ +import { vi } from 'vitest'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { ExecutionContext } from '@nestjs/common'; +import { HttpAdapterHost, Reflector } from '@nestjs/core'; +import { Test, TestingModule } from '@nestjs/testing'; +import { HttpCacheInterceptor } from './http-cache.interceptor'; + +describe('HttpCacheInterceptor', () => { + let interceptor: HttpCacheInterceptor; + + const mockHttpAdapter = { + getRequestMethod: vi.fn(), + getRequestUrl: vi.fn(), + }; + + const createMockContext = (): ExecutionContext => + ({ + switchToHttp: () => ({ + getRequest: () => ({}), + }), + }) as unknown as ExecutionContext; + + beforeEach(async () => { + vi.clearAllMocks(); + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + HttpCacheInterceptor, + { + provide: CACHE_MANAGER, + useValue: { get: vi.fn(), set: vi.fn() }, + }, + { + provide: Reflector, + useValue: new Reflector(), + }, + { + provide: HttpAdapterHost, + useValue: { httpAdapter: mockHttpAdapter }, + }, + ], + }).compile(); + + interceptor = module.get(HttpCacheInterceptor); + }); + + it('should be defined', () => { + expect(interceptor).toBeDefined(); + }); + + describe('trackBy', () => { + it('should return the request URL for GET requests', () => { + mockHttpAdapter.getRequestMethod.mockReturnValue('GET'); + mockHttpAdapter.getRequestUrl.mockReturnValue('/'); + + const result = interceptor.trackBy(createMockContext()); + + expect(result).toBe('/'); + }); + + it('should return undefined for POST requests', () => { + mockHttpAdapter.getRequestMethod.mockReturnValue('POST'); + mockHttpAdapter.getRequestUrl.mockReturnValue('/'); + + const result = interceptor.trackBy(createMockContext()); + + expect(result).toBeUndefined(); + }); + + it('should return undefined for PUT requests', () => { + mockHttpAdapter.getRequestMethod.mockReturnValue('PUT'); + mockHttpAdapter.getRequestUrl.mockReturnValue('/resource'); + + const result = interceptor.trackBy(createMockContext()); + + expect(result).toBeUndefined(); + }); + + it('should return undefined for DELETE requests', () => { + mockHttpAdapter.getRequestMethod.mockReturnValue('DELETE'); + mockHttpAdapter.getRequestUrl.mockReturnValue('/resource/1'); + + const result = interceptor.trackBy(createMockContext()); + + expect(result).toBeUndefined(); + }); + + it('should return different URLs for different GET paths', () => { + mockHttpAdapter.getRequestMethod.mockReturnValue('GET'); + mockHttpAdapter.getRequestUrl.mockReturnValue('/api/items'); + + const result = interceptor.trackBy(createMockContext()); + + expect(result).toBe('/api/items'); + }); + }); +}); diff --git a/sample/20-cache/src/common/http-cache.interceptor.ts b/sample/20-cache/src/common/http-cache.interceptor.ts index f2bc578efa6..36e39d2f5b4 100644 --- a/sample/20-cache/src/common/http-cache.interceptor.ts +++ b/sample/20-cache/src/common/http-cache.interceptor.ts @@ -8,7 +8,7 @@ export class HttpCacheInterceptor extends CacheInterceptor { const { httpAdapter } = this.httpAdapterHost; const isGetRequest = httpAdapter.getRequestMethod(request) === 'GET'; - const excludePaths = [ + const excludePaths: string[] = [ // Routes to be excluded ]; if ( diff --git a/sample/20-cache/src/main.ts b/sample/20-cache/src/main.ts index ac2486c640c..261ee54ad60 100644 --- a/sample/20-cache/src/main.ts +++ b/sample/20-cache/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/20-cache/tsconfig.json b/sample/20-cache/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/20-cache/tsconfig.json +++ b/sample/20-cache/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/20-cache/vitest.config.e2e.mts b/sample/20-cache/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/20-cache/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/20-cache/vitest.config.mts b/sample/20-cache/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/20-cache/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/21-serializer/e2e/app/app.e2e-spec.ts b/sample/21-serializer/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..837df7db4ce --- /dev/null +++ b/sample/21-serializer/e2e/app/app.e2e-spec.ts @@ -0,0 +1,51 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return a serialized user with expected fields', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.body).toMatchObject({ + id: 1, + firstName: 'Kamil', + lastName: 'Mysliwiec', + }); + }); + + it('should exclude the password field', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.body).not.toHaveProperty('password'); + }); + + it('should expose the computed fullName field', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.body.fullName).toBe('Kamil Mysliwiec'); + }); + + it('should transform the role to its name string', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.body.role).toBe('admin'); + }); + }); +}); diff --git a/sample/21-serializer/eslint.config.mjs b/sample/21-serializer/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/21-serializer/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/21-serializer/package.json b/sample/21-serializer/package.json index 0deb311b4b2..dd2eb67822c 100644 --- a/sample/21-serializer/package.json +++ b/sample/21-serializer/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -28,27 +28,21 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "eslint-plugin-import": "2.32.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-import": "2.32.0", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/21-serializer/src/app.controller.ts b/sample/21-serializer/src/app.controller.ts index db71a49287a..1ede03382e3 100644 --- a/sample/21-serializer/src/app.controller.ts +++ b/sample/21-serializer/src/app.controller.ts @@ -4,8 +4,8 @@ import { Get, UseInterceptors, } from '@nestjs/common'; -import { RoleEntity } from './entities/role.entity'; -import { UserEntity } from './entities/user.entity'; +import { RoleEntity } from './entities/role.entity.js'; +import { UserEntity } from './entities/user.entity.js'; @Controller() @UseInterceptors(ClassSerializerInterceptor) diff --git a/sample/21-serializer/src/app.module.ts b/sample/21-serializer/src/app.module.ts index 848d4aaa7fe..52bb569b5d1 100644 --- a/sample/21-serializer/src/app.module.ts +++ b/sample/21-serializer/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; @Module({ controllers: [AppController], diff --git a/sample/21-serializer/src/entities/user.entity.ts b/sample/21-serializer/src/entities/user.entity.ts index d1e67bf5b02..0551f9f356b 100644 --- a/sample/21-serializer/src/entities/user.entity.ts +++ b/sample/21-serializer/src/entities/user.entity.ts @@ -1,5 +1,5 @@ import { Exclude, Expose, Transform } from 'class-transformer'; -import { RoleEntity } from './role.entity'; +import { RoleEntity } from './role.entity.js'; export class UserEntity { id: number; diff --git a/sample/21-serializer/src/main.ts b/sample/21-serializer/src/main.ts index ac2486c640c..261ee54ad60 100644 --- a/sample/21-serializer/src/main.ts +++ b/sample/21-serializer/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/21-serializer/tsconfig.json b/sample/21-serializer/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/21-serializer/tsconfig.json +++ b/sample/21-serializer/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/21-serializer/vitest.config.e2e.mts b/sample/21-serializer/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/21-serializer/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/21-serializer/vitest.config.mts b/sample/21-serializer/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/21-serializer/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/22-graphql-prisma/.gitignore b/sample/22-graphql-prisma/.gitignore index d64fba58335..5628cb1784c 100755 --- a/sample/22-graphql-prisma/.gitignore +++ b/sample/22-graphql-prisma/.gitignore @@ -20,6 +20,9 @@ npm-debug.log # dist /dist +# generated prisma client +/src/generated + # database prisma/dev.db diff --git a/sample/22-graphql-prisma/eslint.config.mjs b/sample/22-graphql-prisma/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/22-graphql-prisma/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/22-graphql-prisma/package.json b/sample/22-graphql-prisma/package.json index ee94934799e..346b9f5c0d4 100644 --- a/sample/22-graphql-prisma/package.json +++ b/sample/22-graphql-prisma/package.json @@ -4,7 +4,7 @@ "description": "Nest TypeScript starter repository", "license": "MIT", "scripts": { - "prebuild": "rimraf dist", + "prebuild": "rimraf dist && prisma generate", "build": "nest build", "generate:typings": "ts-node generate-typings.ts", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", @@ -12,12 +12,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/server": "5.4.0", @@ -26,8 +26,8 @@ "@nestjs/core": "11.1.13", "@nestjs/graphql": "13.2.4", "@nestjs/platform-express": "11.1.13", - "@prisma/adapter-better-sqlite3": "7.4.0", - "@prisma/client": "7.4.0", + "@prisma/adapter-better-sqlite3": "^7.0.0", + "@prisma/client": "^7.0.0", "class-transformer": "0.5.1", "class-validator": "0.14.3", "graphql": "16.10.0", @@ -37,31 +37,23 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "@typescript-eslint/eslint-plugin": "8.55.0", - "@typescript-eslint/parser": "8.55.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "prisma": "^7.0.0", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-morph": "27.0.2", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/22-graphql-prisma/prisma/schema.prisma b/sample/22-graphql-prisma/prisma/schema.prisma index 0f7b7ed852e..561ed68b62b 100755 --- a/sample/22-graphql-prisma/prisma/schema.prisma +++ b/sample/22-graphql-prisma/prisma/schema.prisma @@ -6,8 +6,8 @@ datasource db { } generator client { - provider = "prisma-client-js" - moduleFormat = "cjs" + provider = "prisma-client" + output = "../src/generated/prisma" } model Post { diff --git a/sample/22-graphql-prisma/src/app.module.ts b/sample/22-graphql-prisma/src/app.module.ts index b8ea4ccb2e5..9fca84a0225 100755 --- a/sample/22-graphql-prisma/src/app.module.ts +++ b/sample/22-graphql-prisma/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; -import { PostsModule } from './posts/posts.module'; +import { PostsModule } from './posts/posts.module.js'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; @Module({ diff --git a/sample/22-graphql-prisma/src/main.ts b/sample/22-graphql-prisma/src/main.ts index c442b692c33..a3b182ae89f 100755 --- a/sample/22-graphql-prisma/src/main.ts +++ b/sample/22-graphql-prisma/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -10,4 +10,4 @@ async function bootstrap() { console.log(`Application is running on: ${await app.getUrl()}`); console.log(`GraphQL Playground: ${await app.getUrl()}/graphql`); } -bootstrap(); +await bootstrap(); diff --git a/sample/22-graphql-prisma/src/posts/posts.module.ts b/sample/22-graphql-prisma/src/posts/posts.module.ts index a70df60583e..39a44376121 100755 --- a/sample/22-graphql-prisma/src/posts/posts.module.ts +++ b/sample/22-graphql-prisma/src/posts/posts.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { PostsResolvers } from './posts.resolvers'; -import { PostsService } from './posts.service'; -import { PrismaModule } from '../prisma/prisma.module'; +import { PostsResolvers } from './posts.resolvers.js'; +import { PostsService } from './posts.service.js'; +import { PrismaModule } from '../prisma/prisma.module.js'; @Module({ providers: [PostsResolvers, PostsService], diff --git a/sample/22-graphql-prisma/src/posts/posts.resolvers.ts b/sample/22-graphql-prisma/src/posts/posts.resolvers.ts index 7516c128310..0d3cbf8b828 100755 --- a/sample/22-graphql-prisma/src/posts/posts.resolvers.ts +++ b/sample/22-graphql-prisma/src/posts/posts.resolvers.ts @@ -1,7 +1,7 @@ import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql'; import { PubSub } from 'graphql-subscriptions'; -import { NewPost, Post, UpdatePost } from 'src/graphql.schema'; -import { PostsService } from './posts.service'; +import { NewPost, Post, UpdatePost } from '../graphql.schema.js'; +import { PostsService } from './posts.service.js'; const pubSub = new PubSub(); diff --git a/sample/22-graphql-prisma/src/posts/posts.service.ts b/sample/22-graphql-prisma/src/posts/posts.service.ts index 633065a2845..c8ab8777da4 100755 --- a/sample/22-graphql-prisma/src/posts/posts.service.ts +++ b/sample/22-graphql-prisma/src/posts/posts.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { Post } from '@prisma/client'; -import { NewPost, UpdatePost } from 'src/graphql.schema'; -import { PrismaService } from '../prisma/prisma.service'; +import { Post } from '../generated/prisma/client.js'; +import { NewPost, UpdatePost } from '../graphql.schema.js'; +import { PrismaService } from '../prisma/prisma.service.js'; @Injectable() export class PostsService { diff --git a/sample/22-graphql-prisma/src/prisma/prisma.module.ts b/sample/22-graphql-prisma/src/prisma/prisma.module.ts index ec0ce329154..00b08f806a7 100755 --- a/sample/22-graphql-prisma/src/prisma/prisma.module.ts +++ b/sample/22-graphql-prisma/src/prisma/prisma.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { PrismaService } from './prisma.service'; +import { PrismaService } from './prisma.service.js'; @Module({ providers: [PrismaService], diff --git a/sample/22-graphql-prisma/src/prisma/prisma.service.ts b/sample/22-graphql-prisma/src/prisma/prisma.service.ts index 80cac9467be..3251f048d32 100755 --- a/sample/22-graphql-prisma/src/prisma/prisma.service.ts +++ b/sample/22-graphql-prisma/src/prisma/prisma.service.ts @@ -1,6 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'; -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from '../generated/prisma/client.js'; const adapter = new PrismaBetterSqlite3({ url: 'file:./prisma/dev.db', diff --git a/sample/22-graphql-prisma/tsconfig.json b/sample/22-graphql-prisma/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100755 --- a/sample/22-graphql-prisma/tsconfig.json +++ b/sample/22-graphql-prisma/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/22-graphql-prisma/vitest.config.mts b/sample/22-graphql-prisma/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/22-graphql-prisma/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/23-graphql-code-first/eslint.config.mjs b/sample/23-graphql-code-first/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/23-graphql-code-first/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/23-graphql-code-first/package.json b/sample/23-graphql-code-first/package.json index 730c9099b73..870d3be434a 100644 --- a/sample/23-graphql-code-first/package.json +++ b/sample/23-graphql-code-first/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/server": "5.4.0", @@ -34,27 +34,22 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "eslint": "9.39.2", "eslint-plugin-import": "2.32.0", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "rimraf": "6.1.2", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/23-graphql-code-first/src/app.module.ts b/sample/23-graphql-code-first/src/app.module.ts index 2b32aa27251..c78d873e70d 100644 --- a/sample/23-graphql-code-first/src/app.module.ts +++ b/sample/23-graphql-code-first/src/app.module.ts @@ -2,8 +2,8 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { DirectiveLocation, GraphQLDirective } from 'graphql'; -import { upperDirectiveTransformer } from './common/directives/upper-case.directive'; -import { RecipesModule } from './recipes/recipes.module'; +import { upperDirectiveTransformer } from './common/directives/upper-case.directive.js'; +import { RecipesModule } from './recipes/recipes.module.js'; @Module({ imports: [ diff --git a/sample/23-graphql-code-first/src/main.ts b/sample/23-graphql-code-first/src/main.ts index ac2486c640c..261ee54ad60 100644 --- a/sample/23-graphql-code-first/src/main.ts +++ b/sample/23-graphql-code-first/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/23-graphql-code-first/src/recipes/recipes.module.ts b/sample/23-graphql-code-first/src/recipes/recipes.module.ts index fd2d2cc95f6..e3b3c691fe3 100644 --- a/sample/23-graphql-code-first/src/recipes/recipes.module.ts +++ b/sample/23-graphql-code-first/src/recipes/recipes.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { DateScalar } from '../common/scalars/date.scalar'; -import { RecipesResolver } from './recipes.resolver'; -import { RecipesService } from './recipes.service'; +import { DateScalar } from '../common/scalars/date.scalar.js'; +import { RecipesResolver } from './recipes.resolver.js'; +import { RecipesService } from './recipes.service.js'; @Module({ providers: [RecipesResolver, RecipesService, DateScalar], diff --git a/sample/23-graphql-code-first/src/recipes/recipes.resolver.ts b/sample/23-graphql-code-first/src/recipes/recipes.resolver.ts index 4634934cbed..7d7b13e0844 100644 --- a/sample/23-graphql-code-first/src/recipes/recipes.resolver.ts +++ b/sample/23-graphql-code-first/src/recipes/recipes.resolver.ts @@ -1,10 +1,10 @@ import { NotFoundException } from '@nestjs/common'; import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql'; import { PubSub } from 'graphql-subscriptions'; -import { NewRecipeInput } from './dto/new-recipe.input'; -import { RecipesArgs } from './dto/recipes.args'; -import { Recipe } from './models/recipe.model'; -import { RecipesService } from './recipes.service'; +import { NewRecipeInput } from './dto/new-recipe.input.js'; +import { RecipesArgs } from './dto/recipes.args.js'; +import { Recipe } from './models/recipe.model.js'; +import { RecipesService } from './recipes.service.js'; const pubSub = new PubSub(); diff --git a/sample/23-graphql-code-first/src/recipes/recipes.service.ts b/sample/23-graphql-code-first/src/recipes/recipes.service.ts index a4d6428eb55..ea609171792 100644 --- a/sample/23-graphql-code-first/src/recipes/recipes.service.ts +++ b/sample/23-graphql-code-first/src/recipes/recipes.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { NewRecipeInput } from './dto/new-recipe.input'; -import { RecipesArgs } from './dto/recipes.args'; -import { Recipe } from './models/recipe.model'; +import { NewRecipeInput } from './dto/new-recipe.input.js'; +import { RecipesArgs } from './dto/recipes.args.js'; +import { Recipe } from './models/recipe.model.js'; @Injectable() export class RecipesService { diff --git a/sample/23-graphql-code-first/tsconfig.json b/sample/23-graphql-code-first/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/23-graphql-code-first/tsconfig.json +++ b/sample/23-graphql-code-first/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/23-graphql-code-first/vitest.config.mts b/sample/23-graphql-code-first/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/23-graphql-code-first/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/24-serve-static/e2e/app/app.e2e-spec.ts b/sample/24-serve-static/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..6133d2eed96 --- /dev/null +++ b/sample/24-serve-static/e2e/app/app.e2e-spec.ts @@ -0,0 +1,32 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + app.setGlobalPrefix('api'); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /api', () => { + it('should return the hello message', async () => { + const response = await request(app.getHttpServer()) + .get('/api') + .expect(200); + + expect(response.text).toBe('Hello, world!'); + }); + }); +}); diff --git a/sample/24-serve-static/eslint.config.mjs b/sample/24-serve-static/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/24-serve-static/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/24-serve-static/package.json b/sample/24-serve-static/package.json index 6128f2d4777..335f8b7b84b 100644 --- a/sample/24-serve-static/package.json +++ b/sample/24-serve-static/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -30,25 +30,20 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/24-serve-static/src/app.module.ts b/sample/24-serve-static/src/app.module.ts index b068e0b70dc..c301cf74d91 100644 --- a/sample/24-serve-static/src/app.module.ts +++ b/sample/24-serve-static/src/app.module.ts @@ -1,7 +1,10 @@ import { Module } from '@nestjs/common'; import { ServeStaticModule } from '@nestjs/serve-static'; -import { join } from 'path'; -import { AppController } from './app.controller'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; +import { AppController } from './app.controller.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); @Module({ imports: [ diff --git a/sample/24-serve-static/src/main.ts b/sample/24-serve-static/src/main.ts index 6edd17a5bf3..6e7de75b28d 100644 --- a/sample/24-serve-static/src/main.ts +++ b/sample/24-serve-static/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -8,4 +8,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/24-serve-static/tsconfig.json b/sample/24-serve-static/tsconfig.json index d9c82ca9758..7826296e686 100644 --- a/sample/24-serve-static/tsconfig.json +++ b/sample/24-serve-static/tsconfig.json @@ -1,17 +1,25 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals", + "node" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/24-serve-static/vitest.config.e2e.mts b/sample/24-serve-static/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/24-serve-static/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/24-serve-static/vitest.config.mts b/sample/24-serve-static/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/24-serve-static/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/25-dynamic-modules/config/test.env b/sample/25-dynamic-modules/config/test.env new file mode 100644 index 00000000000..b7e72fc1d0d --- /dev/null +++ b/sample/25-dynamic-modules/config/test.env @@ -0,0 +1 @@ +HELLO_MESSAGE = 'Hello there, world!' \ No newline at end of file diff --git a/sample/25-dynamic-modules/e2e/app/app.e2e-spec.ts b/sample/25-dynamic-modules/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..2896e940a9f --- /dev/null +++ b/sample/25-dynamic-modules/e2e/app/app.e2e-spec.ts @@ -0,0 +1,36 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }, 30000); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return the hello message from config', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.text).toBe('Hello there, world!'); + }); + + it('should return a string', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(typeof response.text).toBe('string'); + expect(response.text.length).toBeGreaterThan(0); + }); + }); +}); diff --git a/sample/25-dynamic-modules/eslint.config.mjs b/sample/25-dynamic-modules/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/25-dynamic-modules/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/25-dynamic-modules/package.json b/sample/25-dynamic-modules/package.json index 0cc47756b4a..931d3b9157a 100644 --- a/sample/25-dynamic-modules/package.json +++ b/sample/25-dynamic-modules/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -28,40 +28,21 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "eslint-plugin-import": "2.32.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-import": "2.32.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/25-dynamic-modules/src/app.controller.spec.ts b/sample/25-dynamic-modules/src/app.controller.spec.ts index 21b61c0d4ec..e38d6c9c84e 100644 --- a/sample/25-dynamic-modules/src/app.controller.spec.ts +++ b/sample/25-dynamic-modules/src/app.controller.spec.ts @@ -1,9 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { AppController } from './app.controller.js'; +import { AppService } from './app.service.js'; const appServiceMock = { - getHello: jest.fn().mockImplementation(() => 'Hello World!'), + getHello: vi.fn().mockImplementation(() => 'Hello World!'), }; describe('AppController', () => { diff --git a/sample/25-dynamic-modules/src/app.controller.ts b/sample/25-dynamic-modules/src/app.controller.ts index cce879ee622..22fbf4d55c3 100644 --- a/sample/25-dynamic-modules/src/app.controller.ts +++ b/sample/25-dynamic-modules/src/app.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; +import { AppService } from './app.service.js'; @Controller() export class AppController { diff --git a/sample/25-dynamic-modules/src/app.module.ts b/sample/25-dynamic-modules/src/app.module.ts index f72e9fe63c3..909973f4006 100644 --- a/sample/25-dynamic-modules/src/app.module.ts +++ b/sample/25-dynamic-modules/src/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { ConfigModule } from './config/config.module'; +import { AppController } from './app.controller.js'; +import { AppService } from './app.service.js'; +import { ConfigModule } from './config/config.module.js'; @Module({ imports: [ConfigModule.register({ folder: './config' })], diff --git a/sample/25-dynamic-modules/src/app.service.ts b/sample/25-dynamic-modules/src/app.service.ts index 73206497238..8d188a942d5 100644 --- a/sample/25-dynamic-modules/src/app.service.ts +++ b/sample/25-dynamic-modules/src/app.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ConfigService } from './config/config.service'; +import { ConfigService } from './config/config.service.js'; @Injectable() export class AppService { diff --git a/sample/25-dynamic-modules/src/config/config.module.ts b/sample/25-dynamic-modules/src/config/config.module.ts index 165d2e0b20a..4f5008dd0c7 100644 --- a/sample/25-dynamic-modules/src/config/config.module.ts +++ b/sample/25-dynamic-modules/src/config/config.module.ts @@ -1,6 +1,6 @@ import { DynamicModule, Module } from '@nestjs/common'; -import { ConfigService } from './config.service'; -import { CONFIG_OPTIONS } from './constants'; +import { ConfigService } from './config.service.js'; +import { CONFIG_OPTIONS } from './constants.js'; export interface ConfigModuleOptions { folder: string; diff --git a/sample/25-dynamic-modules/src/config/config.service.spec.ts b/sample/25-dynamic-modules/src/config/config.service.spec.ts index efbfc402301..538f73624c3 100644 --- a/sample/25-dynamic-modules/src/config/config.service.spec.ts +++ b/sample/25-dynamic-modules/src/config/config.service.spec.ts @@ -1,9 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ConfigService } from './config.service'; -import { CONFIG_OPTIONS } from './constants'; +import { ConfigService } from './config.service.js'; +import { CONFIG_OPTIONS } from './constants.js'; -jest.mock('dotenv'); -jest.mock('fs'); +vi.mock('dotenv'); +vi.mock('fs'); describe('ConfigService', () => { let service: ConfigService; diff --git a/sample/25-dynamic-modules/src/config/config.service.ts b/sample/25-dynamic-modules/src/config/config.service.ts index dff4eb7ddd6..f164dd68636 100644 --- a/sample/25-dynamic-modules/src/config/config.service.ts +++ b/sample/25-dynamic-modules/src/config/config.service.ts @@ -2,8 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import * as dotenv from 'dotenv'; import * as fs from 'fs'; import * as path from 'path'; -import { CONFIG_OPTIONS } from './constants'; -import { ConfigOptions, EnvConfig } from './interfaces'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { CONFIG_OPTIONS } from './constants.js'; +import { ConfigOptions, EnvConfig } from './interfaces/index.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); @Injectable() export class ConfigService { diff --git a/sample/25-dynamic-modules/src/config/interfaces/index.ts b/sample/25-dynamic-modules/src/config/interfaces/index.ts index f6a4a06f2c8..2c1ca9cb260 100644 --- a/sample/25-dynamic-modules/src/config/interfaces/index.ts +++ b/sample/25-dynamic-modules/src/config/interfaces/index.ts @@ -1,2 +1,2 @@ -export * from './envconfig.interface'; -export * from './config-options.interface'; +export * from './envconfig.interface.js'; +export * from './config-options.interface.js'; diff --git a/sample/25-dynamic-modules/src/main.ts b/sample/25-dynamic-modules/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/25-dynamic-modules/src/main.ts +++ b/sample/25-dynamic-modules/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/25-dynamic-modules/tsconfig.json b/sample/25-dynamic-modules/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/25-dynamic-modules/tsconfig.json +++ b/sample/25-dynamic-modules/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/25-dynamic-modules/vitest.config.e2e.mts b/sample/25-dynamic-modules/vitest.config.e2e.mts new file mode 100644 index 00000000000..0f98631f448 --- /dev/null +++ b/sample/25-dynamic-modules/vitest.config.e2e.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + hookTimeout: 30000, + } +}); diff --git a/sample/25-dynamic-modules/vitest.config.mts b/sample/25-dynamic-modules/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/25-dynamic-modules/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/26-queues/e2e/audio/audio.e2e-spec.ts b/sample/26-queues/e2e/audio/audio.e2e-spec.ts index 59fabe318dd..b8d64019861 100644 --- a/sample/26-queues/e2e/audio/audio.e2e-spec.ts +++ b/sample/26-queues/e2e/audio/audio.e2e-spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../../src/app.module'; +import request from 'supertest'; +import { AppModule } from './../../src/app.module.js'; describe('AudioController (e2e)', () => { let app: INestApplication; @@ -21,9 +21,7 @@ describe('AudioController (e2e)', () => { describe('/audio/transcode (POST)', () => { it('should queue a transcoding job and return success', () => { - return request(app.getHttpServer()) - .post('/audio/transcode') - .expect(201); + return request(app.getHttpServer()).post('/audio/transcode').expect(201); }); it('should handle multiple concurrent requests', async () => { @@ -35,9 +33,7 @@ describe('AudioController (e2e)', () => { }); it('should reject GET requests', () => { - return request(app.getHttpServer()) - .get('/audio/transcode') - .expect(404); + return request(app.getHttpServer()).get('/audio/transcode').expect(404); }); }); }); diff --git a/sample/26-queues/e2e/jest-e2e.json b/sample/26-queues/e2e/jest-e2e.json deleted file mode 100644 index 9f2a9d0af2f..00000000000 --- a/sample/26-queues/e2e/jest-e2e.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "testTimeout": 30000, - "detectOpenHandles": true, - "forceExit": true -} diff --git a/sample/26-queues/eslint.config.mjs b/sample/26-queues/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/26-queues/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/26-queues/package.json b/sample/26-queues/package.json index 9473abb0031..cf6c9282964 100644 --- a/sample/26-queues/package.json +++ b/sample/26-queues/package.json @@ -4,19 +4,19 @@ "description": "Nest TypeScript starter repository", "license": "MIT", "scripts": { - "prebuild": "rimraf dist", - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest --config jest.json", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./e2e/jest-e2e.json --forceExit" + "prebuild": "rimraf dist", + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/bull": "11.0.4", @@ -30,41 +30,22 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/bull": "4.10.4", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "eslint-plugin-import": "2.32.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-import": "2.32.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/26-queues/src/app.module.ts b/sample/26-queues/src/app.module.ts index db04ab261e4..4a0f17ee301 100644 --- a/sample/26-queues/src/app.module.ts +++ b/sample/26-queues/src/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { BullModule } from '@nestjs/bull'; -import { AudioController } from './audio/audio.controller'; -import { AudioProcessor } from './audio/audio.processor'; +import { AudioController } from './audio/audio.controller.js'; +import { AudioProcessor } from './audio/audio.processor.js'; @Module({ imports: [ diff --git a/sample/26-queues/src/audio/audio.controller.spec.ts b/sample/26-queues/src/audio/audio.controller.spec.ts index 8cd9824cb5a..4d4f5afbe9f 100644 --- a/sample/26-queues/src/audio/audio.controller.spec.ts +++ b/sample/26-queues/src/audio/audio.controller.spec.ts @@ -1,14 +1,14 @@ import { getQueueToken } from '@nestjs/bull'; import { Test, TestingModule } from '@nestjs/testing'; import { Queue } from 'bull'; -import { AudioController } from './audio.controller'; +import { AudioController } from './audio.controller.js'; describe('AudioController', () => { let audioController: AudioController; let audioQueue: Queue; const mockQueue = { - add: jest.fn(), + add: vi.fn(), }; beforeEach(async () => { @@ -27,7 +27,7 @@ describe('AudioController', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('transcode', () => { diff --git a/sample/26-queues/src/audio/audio.module.ts b/sample/26-queues/src/audio/audio.module.ts index c488db41acc..16b2c0f5e9e 100644 --- a/sample/26-queues/src/audio/audio.module.ts +++ b/sample/26-queues/src/audio/audio.module.ts @@ -1,7 +1,7 @@ import { BullModule } from '@nestjs/bull'; import { Module } from '@nestjs/common'; -import { AudioController } from './audio.controller'; -import { AudioProcessor } from './audio.processor'; +import { AudioController } from './audio.controller.js'; +import { AudioProcessor } from './audio.processor.js'; @Module({ imports: [ diff --git a/sample/26-queues/src/audio/audio.processor.spec.ts b/sample/26-queues/src/audio/audio.processor.spec.ts index d7fd6bef6d9..37893fccdf5 100644 --- a/sample/26-queues/src/audio/audio.processor.spec.ts +++ b/sample/26-queues/src/audio/audio.processor.spec.ts @@ -1,11 +1,12 @@ +import { MockInstance } from 'vitest'; import { Logger } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Job } from 'bull'; -import { AudioProcessor } from './audio.processor'; +import { AudioProcessor } from './audio.processor.js'; describe('AudioProcessor', () => { let audioProcessor: AudioProcessor; - let loggerSpy: jest.SpyInstance; + let loggerSpy: MockInstance; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -13,11 +14,11 @@ describe('AudioProcessor', () => { }).compile(); audioProcessor = module.get(AudioProcessor); - loggerSpy = jest.spyOn(Logger.prototype, 'debug').mockImplementation(); + loggerSpy = vi.spyOn(Logger.prototype, 'debug').mockImplementation(); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('handleTranscode', () => { diff --git a/sample/26-queues/src/main.ts b/sample/26-queues/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/26-queues/src/main.ts +++ b/sample/26-queues/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/26-queues/tsconfig.json b/sample/26-queues/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/26-queues/tsconfig.json +++ b/sample/26-queues/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/26-queues/vitest.config.e2e.mts b/sample/26-queues/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/26-queues/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/26-queues/vitest.config.mts b/sample/26-queues/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/26-queues/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/27-scheduling/e2e/app/app.e2e-spec.ts b/sample/27-scheduling/e2e/app/app.e2e-spec.ts new file mode 100644 index 00000000000..e0f31fce104 --- /dev/null +++ b/sample/27-scheduling/e2e/app/app.e2e-spec.ts @@ -0,0 +1,46 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { SchedulerRegistry } from '@nestjs/schedule'; +import { AppModule } from '../../src/app.module.js'; +import { TasksService } from '../../src/tasks/tasks.service.js'; + +describe('TasksService (e2e)', () => { + let app: INestApplication; + let module: TestingModule; + let schedulerRegistry: SchedulerRegistry; + + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = module.createNestApplication(); + await app.init(); + + schedulerRegistry = module.get(SchedulerRegistry); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should have TasksService registered', () => { + const tasksService = module.get(TasksService); + expect(tasksService).toBeDefined(); + }); + + it('should register a cron job', () => { + const cronJobs = schedulerRegistry.getCronJobs(); + expect(cronJobs.size).toBeGreaterThanOrEqual(1); + }); + + it('should register an interval', () => { + const intervals = schedulerRegistry.getIntervals(); + expect(intervals.length).toBeGreaterThanOrEqual(1); + }); + + it('should register a timeout', () => { + const timeouts = schedulerRegistry.getTimeouts(); + expect(timeouts.length).toBeGreaterThanOrEqual(1); + }); +}); diff --git a/sample/27-scheduling/eslint.config.mjs b/sample/27-scheduling/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/27-scheduling/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/27-scheduling/package.json b/sample/27-scheduling/package.json index 4baeb0f0fd6..a0e265c2d70 100644 --- a/sample/27-scheduling/package.json +++ b/sample/27-scheduling/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -29,41 +29,22 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/bull": "4.10.4", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "eslint-plugin-import": "2.32.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-import": "2.32.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/27-scheduling/src/app.module.ts b/sample/27-scheduling/src/app.module.ts index 944d46d2ce3..596ab73c6cb 100644 --- a/sample/27-scheduling/src/app.module.ts +++ b/sample/27-scheduling/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { ScheduleModule } from '@nestjs/schedule'; -import { TasksModule } from './tasks/tasks.module'; +import { TasksModule } from './tasks/tasks.module.js'; @Module({ imports: [ScheduleModule.forRoot(), TasksModule], diff --git a/sample/27-scheduling/src/main.ts b/sample/27-scheduling/src/main.ts index e1e10d96995..ab748192404 100644 --- a/sample/27-scheduling/src/main.ts +++ b/sample/27-scheduling/src/main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/27-scheduling/src/tasks/tasks.module.ts b/sample/27-scheduling/src/tasks/tasks.module.ts index 51c8ab951c0..bb3103babc0 100644 --- a/sample/27-scheduling/src/tasks/tasks.module.ts +++ b/sample/27-scheduling/src/tasks/tasks.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { TasksService } from './tasks.service'; +import { TasksService } from './tasks.service.js'; @Module({ providers: [TasksService], diff --git a/sample/27-scheduling/tsconfig.json b/sample/27-scheduling/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/27-scheduling/tsconfig.json +++ b/sample/27-scheduling/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/27-scheduling/vitest.config.e2e.mts b/sample/27-scheduling/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/27-scheduling/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/27-scheduling/vitest.config.mts b/sample/27-scheduling/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/27-scheduling/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/28-sse/e2e/app.e2e-spec.ts b/sample/28-sse/e2e/app.e2e-spec.ts new file mode 100644 index 00000000000..f172d66c544 --- /dev/null +++ b/sample/28-sse/e2e/app.e2e-spec.ts @@ -0,0 +1,74 @@ +import { INestApplication } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AddressInfo } from 'net'; +import http from 'http'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /', () => { + it('should return 200 with HTML content', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('content-type', /text\/html/) + .expect((res) => { + expect(res.text).toContain('EventSource'); + }); + }); + }); + + describe('GET /sse', () => { + it('should return text/event-stream and emit event data', () => { + return new Promise((resolve, reject) => { + const httpServer = app.getHttpServer(); + if (!httpServer.listening) httpServer.listen(0); + const { port } = httpServer.address() as AddressInfo; + + let received = ''; + let completed = false; + + const finish = (err?: Error) => { + if (!completed) { + completed = true; + if (err) reject(err); + else resolve(); + } + }; + + const req = http.get(`http://127.0.0.1:${port}/sse`, (res) => { + expect(res.statusCode).toBe(200); + expect(res.headers['content-type']).toContain('text/event-stream'); + + res.on('data', (chunk: Buffer) => { + received += chunk.toString(); + if (received.includes('"hello":"world"')) { + expect(received).toContain('"hello":"world"'); + req.destroy(); + finish(); + } + }); + }); + + req.on('error', (err: NodeJS.ErrnoException) => { + if (err.code !== 'ECONNRESET') finish(err); + }); + }); + }, 5000); + }); +}); diff --git a/sample/28-sse/eslint.config.mjs b/sample/28-sse/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/28-sse/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/28-sse/package.json b/sample/28-sse/package.json index 065ac8bd706..3f1ec04ec16 100644 --- a/sample/28-sse/package.json +++ b/sample/28-sse/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -27,42 +27,21 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "eslint-plugin-import": "2.32.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-import": "2.32.0", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/28-sse/src/app.controller.spec.ts b/sample/28-sse/src/app.controller.spec.ts new file mode 100644 index 00000000000..4296bd5bcb2 --- /dev/null +++ b/sample/28-sse/src/app.controller.spec.ts @@ -0,0 +1,55 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { firstValueFrom } from 'rxjs'; +import { AppController } from './app.controller'; + +describe('AppController', () => { + let controller: AppController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + }).compile(); + + controller = module.get(AppController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + describe('index', () => { + it('should respond with text/html and the index.html content', () => { + const mockResponse = { + type: vi.fn().mockReturnThis(), + send: vi.fn(), + } as any; + + controller.index(mockResponse); + + expect(mockResponse.type).toHaveBeenCalledWith('text/html'); + expect(mockResponse.send).toHaveBeenCalledWith( + expect.stringContaining('EventSource'), + ); + }); + }); + + describe('sse', () => { + it('should return an Observable', () => { + const result = controller.sse(); + expect(result).toBeDefined(); + expect(typeof result.subscribe).toBe('function'); + }); + + it('should emit { data: { hello: "world" } } on each interval tick', async () => { + vi.useFakeTimers(); + + const promise = firstValueFrom(controller.sse()); + vi.advanceTimersByTime(1000); + const event = await promise; + + expect(event).toEqual({ data: { hello: 'world' } }); + + vi.useRealTimers(); + }); + }); +}); diff --git a/sample/28-sse/src/app.controller.ts b/sample/28-sse/src/app.controller.ts index 6a029a9a9d9..baf0bd39e57 100644 --- a/sample/28-sse/src/app.controller.ts +++ b/sample/28-sse/src/app.controller.ts @@ -1,10 +1,13 @@ import { Controller, Get, MessageEvent, Res, Sse } from '@nestjs/common'; import { Response } from 'express'; import { readFileSync } from 'fs'; -import { join } from 'path'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; import { interval, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +const __dirname = dirname(fileURLToPath(import.meta.url)); + @Controller() export class AppController { @Get() diff --git a/sample/28-sse/src/app.module.ts b/sample/28-sse/src/app.module.ts index 98b6ba834e0..4e79341bfad 100644 --- a/sample/28-sse/src/app.module.ts +++ b/sample/28-sse/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; +import { AppController } from './app.controller.js'; @Module({ imports: [], diff --git a/sample/28-sse/src/main.ts b/sample/28-sse/src/main.ts index 4d8008da574..41c3b080674 100644 --- a/sample/28-sse/src/main.ts +++ b/sample/28-sse/src/main.ts @@ -1,5 +1,5 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule, { @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/28-sse/tsconfig.json b/sample/28-sse/tsconfig.json index 1bbdea209f3..9cfc270c9e2 100644 --- a/sample/28-sse/tsconfig.json +++ b/sample/28-sse/tsconfig.json @@ -1,17 +1,28 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist" + ] } diff --git a/sample/28-sse/vitest.config.e2e.mts b/sample/28-sse/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/28-sse/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/28-sse/vitest.config.mts b/sample/28-sse/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/28-sse/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/29-file-upload/e2e/app/app.e2e-spec.ts b/sample/29-file-upload/e2e/app/app.e2e-spec.ts index 415bf033bce..562cb32d6a0 100644 --- a/sample/29-file-upload/e2e/app/app.e2e-spec.ts +++ b/sample/29-file-upload/e2e/app/app.e2e-spec.ts @@ -1,8 +1,8 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { readFileSync } from 'fs'; -import * as request from 'supertest'; -import { AppModule } from '../../src/app.module'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; describe('E2E FileTest', () => { let app: INestApplication; diff --git a/sample/29-file-upload/e2e/jest-e2e.json b/sample/29-file-upload/e2e/jest-e2e.json deleted file mode 100644 index 72eb5c3c0a3..00000000000 --- a/sample/29-file-upload/e2e/jest-e2e.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ], - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", - "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], - "coverageReporters": ["json", "lcov"] -} \ No newline at end of file diff --git a/sample/29-file-upload/eslint.config.mjs b/sample/29-file-upload/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/29-file-upload/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/29-file-upload/package.json b/sample/29-file-upload/package.json index bf187082aa4..cfb394bab2c 100644 --- a/sample/29-file-upload/package.json +++ b/sample/29-file-upload/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --config ./e2e/jest-e2e.json" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -29,46 +29,22 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", "@types/multer": "2.0.0", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "jest": "30.2.0", + "eslint-plugin-import": "2.32.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-import": "2.32.0", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/29-file-upload/src/app.controller.ts b/sample/29-file-upload/src/app.controller.ts index 130fb407ef7..c70d71662bd 100644 --- a/sample/29-file-upload/src/app.controller.ts +++ b/sample/29-file-upload/src/app.controller.ts @@ -8,9 +8,8 @@ import { UseInterceptors, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; -import { Express } from 'express'; -import { AppService } from './app.service'; -import { SampleDto } from './sample.dto'; +import { AppService } from './app.service.js'; +import { SampleDto } from './sample.dto.js'; @Controller() export class AppController { diff --git a/sample/29-file-upload/src/app.module.ts b/sample/29-file-upload/src/app.module.ts index 7845d045f8d..7abec95862b 100644 --- a/sample/29-file-upload/src/app.module.ts +++ b/sample/29-file-upload/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { AppController } from './app.controller.js'; +import { AppService } from './app.service.js'; @Module({ controllers: [AppController], diff --git a/sample/29-file-upload/src/main.ts b/sample/29-file-upload/src/main.ts index ac2486c640c..261ee54ad60 100644 --- a/sample/29-file-upload/src/main.ts +++ b/sample/29-file-upload/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -9,4 +9,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/29-file-upload/tsconfig.json b/sample/29-file-upload/tsconfig.json index d9c82ca9758..1dc9f3905d9 100644 --- a/sample/29-file-upload/tsconfig.json +++ b/sample/29-file-upload/tsconfig.json @@ -1,17 +1,25 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals", + "multer" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/29-file-upload/vitest.config.e2e.mts b/sample/29-file-upload/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/29-file-upload/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/29-file-upload/vitest.config.mts b/sample/29-file-upload/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/29-file-upload/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/30-event-emitter/eslint.config.mjs b/sample/30-event-emitter/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/30-event-emitter/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/30-event-emitter/package.json b/sample/30-event-emitter/package.json index 450bc676fc4..c5c6002a103 100644 --- a/sample/30-event-emitter/package.json +++ b/sample/30-event-emitter/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -28,40 +28,20 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "typescript": "6.0.2", + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/30-event-emitter/src/app.module.ts b/sample/30-event-emitter/src/app.module.ts index fc7b1e54078..411ed750320 100644 --- a/sample/30-event-emitter/src/app.module.ts +++ b/sample/30-event-emitter/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { EventEmitterModule } from '@nestjs/event-emitter'; -import { OrdersModule } from './orders/orders.module'; +import { OrdersModule } from './orders/orders.module.js'; @Module({ imports: [EventEmitterModule.forRoot(), OrdersModule], diff --git a/sample/30-event-emitter/src/main.ts b/sample/30-event-emitter/src/main.ts index 13cad38cff9..524db419b62 100644 --- a/sample/30-event-emitter/src/main.ts +++ b/sample/30-event-emitter/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } -bootstrap(); +await bootstrap(); diff --git a/sample/30-event-emitter/src/orders/listeners/order-created.listener.ts b/sample/30-event-emitter/src/orders/listeners/order-created.listener.ts index 0e9a2ddf8e8..1e02dc2a7a4 100644 --- a/sample/30-event-emitter/src/orders/listeners/order-created.listener.ts +++ b/sample/30-event-emitter/src/orders/listeners/order-created.listener.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; -import { OrderCreatedEvent } from '../events/order-created.event'; +import { OrderCreatedEvent } from '../events/order-created.event.js'; @Injectable() export class OrderCreatedListener { diff --git a/sample/30-event-emitter/src/orders/orders.controller.ts b/sample/30-event-emitter/src/orders/orders.controller.ts index 784e0c03ea9..7dc1105e338 100644 --- a/sample/30-event-emitter/src/orders/orders.controller.ts +++ b/sample/30-event-emitter/src/orders/orders.controller.ts @@ -1,6 +1,6 @@ import { Body, Controller, Post } from '@nestjs/common'; -import { CreateOrderDto } from './dto/create-order.dto'; -import { OrdersService } from './orders.service'; +import { CreateOrderDto } from './dto/create-order.dto.js'; +import { OrdersService } from './orders.service.js'; @Controller('orders') export class OrdersController { diff --git a/sample/30-event-emitter/src/orders/orders.module.ts b/sample/30-event-emitter/src/orders/orders.module.ts index f2fd443fe6e..6ecb4ebf87a 100644 --- a/sample/30-event-emitter/src/orders/orders.module.ts +++ b/sample/30-event-emitter/src/orders/orders.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { OrderCreatedListener } from './listeners/order-created.listener'; -import { OrdersController } from './orders.controller'; -import { OrdersService } from './orders.service'; +import { OrderCreatedListener } from './listeners/order-created.listener.js'; +import { OrdersController } from './orders.controller.js'; +import { OrdersService } from './orders.service.js'; @Module({ controllers: [OrdersController], diff --git a/sample/30-event-emitter/src/orders/orders.service.ts b/sample/30-event-emitter/src/orders/orders.service.ts index e2acb96fc73..d5addc891a9 100644 --- a/sample/30-event-emitter/src/orders/orders.service.ts +++ b/sample/30-event-emitter/src/orders/orders.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { Order } from '../orders/entities/order.entity'; -import { CreateOrderDto } from './dto/create-order.dto'; -import { OrderCreatedEvent } from './events/order-created.event'; +import { Order } from '../orders/entities/order.entity.js'; +import { CreateOrderDto } from './dto/create-order.dto.js'; +import { OrderCreatedEvent } from './events/order-created.event.js'; @Injectable() export class OrdersService { diff --git a/sample/30-event-emitter/test/app.e2e-spec.ts b/sample/30-event-emitter/test/app.e2e-spec.ts index 0136f2d60b6..8e961185a03 100644 --- a/sample/30-event-emitter/test/app.e2e-spec.ts +++ b/sample/30-event-emitter/test/app.e2e-spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; +import request from 'supertest'; +import { AppModule } from './../src/app.module.js'; describe('AppController (e2e)', () => { let app: INestApplication; @@ -15,11 +15,11 @@ describe('AppController (e2e)', () => { await app.init(); }); - it('/ (GET)', () => { + it('/orders (POST)', () => { return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); + .post('/orders') + .send({ name: 'test order', description: 'test' }) + .expect(201); }); afterEach(() => app.close()); diff --git a/sample/30-event-emitter/test/jest-e2e.json b/sample/30-event-emitter/test/jest-e2e.json deleted file mode 100644 index e9d912f3e3c..00000000000 --- a/sample/30-event-emitter/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/sample/30-event-emitter/tsconfig.json b/sample/30-event-emitter/tsconfig.json index 29bde718a4f..697ff9b7000 100644 --- a/sample/30-event-emitter/tsconfig.json +++ b/sample/30-event-emitter/tsconfig.json @@ -1,17 +1,25 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*", "test/**/*"], + "include": [ + "src/**/*", + "test/**/*" + ] } diff --git a/sample/30-event-emitter/vitest.config.e2e.mts b/sample/30-event-emitter/vitest.config.e2e.mts new file mode 100644 index 00000000000..c13699cd867 --- /dev/null +++ b/sample/30-event-emitter/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['test/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/30-event-emitter/vitest.config.mts b/sample/30-event-emitter/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/30-event-emitter/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/31-graphql-federation-code-first/README.md b/sample/31-graphql-federation-code-first/README.md index 2743338c4cb..bbc72be0d59 100644 --- a/sample/31-graphql-federation-code-first/README.md +++ b/sample/31-graphql-federation-code-first/README.md @@ -1,12 +1,25 @@ # GraphQL Federation - Code First -A simple example of GraphQL Federation using Code First approach. +A production-ready example of GraphQL Federation using Code First approach. + +## Installation + +Install dependencies for all applications: + +```sh +cd users-application && npm install +cd ../posts-application && npm install +cd ../gateway && npm install +``` ## Execution -Make sure to start the two sub-graph applications first, then the gateway. Otherwise the gateway won't be able to fetch schemas from the sub-graphs. +### Development Mode + +For development, the gateway will automatically generate a supergraph schema on startup: ```sh +# Start sub-graph applications first cd users-application && npm run start ``` @@ -15,9 +28,39 @@ cd posts-application && npm run start ``` ```sh +# The gateway will auto-generate supergraph.graphql on startup cd gateway && npm run start ``` +### Production Mode + +For production, generate the supergraph schema beforehand: + +```sh +# 1. Ensure sub-graphs are running +cd users-application && npm run start +cd ../posts-application && npm run start + +# 2. Generate supergraph schema using Rover CLI (requires @apollo/rover installation) +cd ../gateway && npm run generate:supergraph:rover + +# 3. Start the gateway with the static supergraph +npm run start:prod +``` + +## Supergraph Schema Generation + +The gateway provides two methods for generating the supergraph schema: + +1. **Local Generation (Development)**: `npm run generate:supergraph` + - Generates a simplified supergraph for local development + - Does not require Rover CLI + +2. **Rover CLI Generation (Production)**: `npm run generate:supergraph:rover` + - Uses Apollo Rover to compose the actual supergraph from running sub-graphs + - Requires Rover CLI: `npm install -g @apollo/rover` + - Recommended for production deployments + ## Access the graph You can reach the gateway under `http://localhost:3001/graphql` diff --git a/sample/31-graphql-federation-code-first/gateway/.gitignore b/sample/31-graphql-federation-code-first/gateway/.gitignore index c16ef026a31..9e640f1adfe 100644 --- a/sample/31-graphql-federation-code-first/gateway/.gitignore +++ b/sample/31-graphql-federation-code-first/gateway/.gitignore @@ -31,4 +31,7 @@ lerna-debug.log* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file +!.vscode/extensions.json + +# Generated files +supergraph.graphql diff --git a/sample/31-graphql-federation-code-first/gateway/eslint.config.mjs b/sample/31-graphql-federation-code-first/gateway/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/31-graphql-federation-code-first/gateway/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/31-graphql-federation-code-first/gateway/generate-supergraph.ts b/sample/31-graphql-federation-code-first/gateway/generate-supergraph.ts new file mode 100644 index 00000000000..d6024981c65 --- /dev/null +++ b/sample/31-graphql-federation-code-first/gateway/generate-supergraph.ts @@ -0,0 +1,109 @@ +import { exec } from 'node:child_process'; +import { promisify } from 'node:util'; +import { writeFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const execAsync = promisify(exec); +const __dirname = dirname(fileURLToPath(import.meta.url)); + +async function generateSupergraph() { + try { + try { + await execAsync('rover --version'); + } catch { + console.error('Rover CLI is not installed. Please install it first:'); + console.error('npm install -g @apollo/rover'); + process.exit(1); + } + + console.log('Generating supergraph schema...'); + const { stdout, stderr } = await execAsync( + 'rover supergraph compose --config ./supergraph.yaml', + ); + + if (stderr && !stderr.includes('WARN')) { + console.error('Error generating supergraph:', stderr); + process.exit(1); + } + + writeFileSync(join(__dirname, 'supergraph.graphql'), stdout); + console.log('Supergraph schema generated successfully!'); + console.log('Output written to: supergraph.graphql'); + } catch (error) { + console.error('Failed to generate supergraph:', error); + process.exit(1); + } +} + +async function generateSupergraphLocal() { + console.log('Generating local supergraph schema for development...'); + + const supergraphSchema = `\ +schema + @link(url: "https://specs.apollo.dev/federation/v2.3") + @link(url: "https://specs.apollo.dev/link/v1.0") +{ + query: Query +} + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +directive @key(fields: String!, resolvable: Boolean = true) on OBJECT | INTERFACE + +directive @requires(fields: String!) on FIELD_DEFINITION + +directive @provides(fields: String!) on FIELD_DEFINITION + +directive @external on OBJECT | FIELD_DEFINITION + +directive @shareable on OBJECT | FIELD_DEFINITION + +directive @extends on OBJECT | INTERFACE + +scalar link__Import + +enum link__Purpose { + SECURITY + EXECUTION +} + +type Query { + _entities(representations: [_Any!]!): [_Entity]! + _service: _Service! +} + +union _Entity = User | Post + +scalar _Any + +type _Service { + sdl: String! +} + +type User @key(fields: "id") { + id: ID! + name: String! + posts: [Post!]! +} + +type Post @key(fields: "id") { + id: ID! + title: String! + body: String! + authorId: ID! + user: User! +} +`; + + writeFileSync(join(__dirname, 'supergraph.graphql'), supergraphSchema); + console.log('Local supergraph schema generated successfully!'); +} + +const useRover = process.argv.includes('--rover'); + +if (useRover) { + await generateSupergraph(); +} else { + await generateSupergraphLocal(); +} diff --git a/sample/31-graphql-federation-code-first/gateway/package.json b/sample/31-graphql-federation-code-first/gateway/package.json index 54aae62805b..ec861425ccb 100644 --- a/sample/31-graphql-federation-code-first/gateway/package.json +++ b/sample/31-graphql-federation-code-first/gateway/package.json @@ -11,12 +11,16 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "generate:supergraph": "ts-node --esm generate-supergraph.ts", + "generate:supergraph:rover": "ts-node --esm generate-supergraph.ts --rover", + "prestart": "npm run generate:supergraph", + "prestart:dev": "npm run generate:supergraph", + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/gateway": "2.13.0", @@ -34,44 +38,21 @@ "ts-morph": "27.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", "typescript": "5.9.3", - "typescript-eslint": "8.55.0", - "webpack": "5.105.2" + "vitest": "4.1.2", + "webpack": "5.105.0" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/31-graphql-federation-code-first/gateway/src/app.module.ts b/sample/31-graphql-federation-code-first/gateway/src/app.module.ts index c4900e92347..844dc4a7c01 100644 --- a/sample/31-graphql-federation-code-first/gateway/src/app.module.ts +++ b/sample/31-graphql-federation-code-first/gateway/src/app.module.ts @@ -1,19 +1,23 @@ -import { IntrospectAndCompose } from '@apollo/gateway'; import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const supergraphSdl = readFileSync( + join(__dirname, '..', 'supergraph.graphql'), + 'utf-8', +).trim(); @Module({ imports: [ GraphQLModule.forRoot({ driver: ApolloGatewayDriver, gateway: { - supergraphSdl: new IntrospectAndCompose({ - subgraphs: [ - { name: 'users', url: 'http://localhost:3002/graphql' }, - { name: 'posts', url: 'http://localhost:3003/graphql' }, - ], - }), + supergraphSdl, }, }), ], diff --git a/sample/31-graphql-federation-code-first/gateway/src/main.ts b/sample/31-graphql-federation-code-first/gateway/src/main.ts index 7d4c1629f4b..178a3e241da 100644 --- a/sample/31-graphql-federation-code-first/gateway/src/main.ts +++ b/sample/31-graphql-federation-code-first/gateway/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3001); } -bootstrap(); +await bootstrap(); diff --git a/sample/31-graphql-federation-code-first/gateway/supergraph.yaml b/sample/31-graphql-federation-code-first/gateway/supergraph.yaml new file mode 100644 index 00000000000..4434d22ec62 --- /dev/null +++ b/sample/31-graphql-federation-code-first/gateway/supergraph.yaml @@ -0,0 +1,10 @@ +federation_version: =2.3.2 +subgraphs: + users: + routing_url: http://localhost:3002/graphql + schema: + subgraph_url: http://localhost:3002/graphql + posts: + routing_url: http://localhost:3003/graphql + schema: + subgraph_url: http://localhost:3003/graphql diff --git a/sample/31-graphql-federation-code-first/gateway/test/app.e2e-spec.ts b/sample/31-graphql-federation-code-first/gateway/test/app.e2e-spec.ts new file mode 100644 index 00000000000..9adf99b3443 --- /dev/null +++ b/sample/31-graphql-federation-code-first/gateway/test/app.e2e-spec.ts @@ -0,0 +1,71 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +describe('GraphQL Federation Gateway (Code First)', () => { + let app: INestApplication; + + beforeAll(async () => { + const supergraphPath = join(__dirname, '..', 'supergraph.graphql'); + if (!existsSync(supergraphPath)) { + await import('../generate-supergraph.js'); + } + + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should load gateway with static supergraph', () => { + return request(app.getHttpServer()) + .post('/graphql') + .send({ + query: ` + query { + _service { + sdl + } + } + `, + }) + .expect(200) + .expect((res) => { + expect(res.body.data).toBeDefined(); + expect(res.body.data._service).toBeDefined(); + expect(res.body.data._service.sdl).toContain('type User'); + expect(res.body.data._service.sdl).toContain('type Post'); + }); + }); + + it('should not use IntrospectAndCompose in production', () => { + const appModulePath = join(__dirname, '..', 'src', 'app.module.ts'); + const appModuleContent = readFileSync(appModulePath, 'utf-8'); + + const hasActiveIntrospect = + appModuleContent.includes('new IntrospectAndCompose') && + !appModuleContent.includes('// supergraphSdl: new IntrospectAndCompose'); + + expect(hasActiveIntrospect).toBe(false); + }); + + it('should read supergraph from file system', () => { + const appModulePath = join(__dirname, '..', 'src', 'app.module.ts'); + const appModuleContent = readFileSync(appModulePath, 'utf-8'); + + expect(appModuleContent).toContain('readFileSync'); + expect(appModuleContent).toContain('supergraph.graphql'); + }); +}); diff --git a/sample/31-graphql-federation-code-first/gateway/tsconfig.json b/sample/31-graphql-federation-code-first/gateway/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/31-graphql-federation-code-first/gateway/tsconfig.json +++ b/sample/31-graphql-federation-code-first/gateway/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/31-graphql-federation-code-first/gateway/vitest.config.e2e.mts b/sample/31-graphql-federation-code-first/gateway/vitest.config.e2e.mts new file mode 100644 index 00000000000..c13699cd867 --- /dev/null +++ b/sample/31-graphql-federation-code-first/gateway/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['test/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/31-graphql-federation-code-first/gateway/vitest.config.mts b/sample/31-graphql-federation-code-first/gateway/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/31-graphql-federation-code-first/gateway/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/31-graphql-federation-code-first/posts-application/eslint.config.mjs b/sample/31-graphql-federation-code-first/posts-application/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/31-graphql-federation-code-first/posts-application/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/31-graphql-federation-code-first/posts-application/package.json b/sample/31-graphql-federation-code-first/posts-application/package.json index aeaecf8811a..8dded904208 100644 --- a/sample/31-graphql-federation-code-first/posts-application/package.json +++ b/sample/31-graphql-federation-code-first/posts-application/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/federation": "0.38.1", @@ -36,43 +36,20 @@ "ts-morph": "27.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.5", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/31-graphql-federation-code-first/posts-application/src/app.module.ts b/sample/31-graphql-federation-code-first/posts-application/src/app.module.ts index ec8d38a4596..0a8aa8e2afc 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/app.module.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { PostsModule } from './posts/posts.module'; +import { PostsModule } from './posts/posts.module.js'; @Module({ imports: [PostsModule], diff --git a/sample/31-graphql-federation-code-first/posts-application/src/main.ts b/sample/31-graphql-federation-code-first/posts-application/src/main.ts index 138096340bb..b867d86b740 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/main.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3003); } -bootstrap(); +await bootstrap(); diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/models/post.model.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/models/post.model.ts index 0962b84c312..90d75b25e53 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/models/post.model.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/models/post.model.ts @@ -1,5 +1,5 @@ import { Directive, Field, ID, Int, ObjectType } from '@nestjs/graphql'; -import { User } from './user.model'; +import { User } from './user.model.js'; @ObjectType() @Directive('@key(fields: "id")') diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/models/user.model.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/models/user.model.ts index 492ea1c8565..8b148cd470e 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/models/user.model.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/models/user.model.ts @@ -1,5 +1,5 @@ import { Directive, Field, ID, ObjectType } from '@nestjs/graphql'; -import { Post } from './post.model'; +import { Post } from './post.model.js'; @ObjectType() @Directive('@extends') diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.module.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.module.ts index da2bfd70bea..788d29d864a 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.module.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.module.ts @@ -5,10 +5,10 @@ import { } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; -import { User } from './models/user.model'; -import { PostsResolver } from './posts.resolver'; -import { PostsService } from './posts.service'; -import { UsersResolver } from './users.resolver'; +import { User } from './models/user.model.js'; +import { PostsResolver } from './posts.resolver.js'; +import { PostsService } from './posts.service.js'; +import { UsersResolver } from './users.resolver.js'; @Module({ imports: [ @@ -17,7 +17,7 @@ import { UsersResolver } from './users.resolver'; autoSchemaFile: { federation: 2, }, - plugins: [ApolloServerPluginInlineTrace()], + plugins: [ApolloServerPluginInlineTrace() as any], buildSchemaOptions: { orphanedTypes: [User], }, diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.spec.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.spec.ts index a1c578c02ea..88872cb028c 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.spec.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Post } from './models/post.model'; -import { PostsResolver } from './posts.resolver'; -import { PostsService } from './posts.service'; +import { Post } from './models/post.model.js'; +import { PostsResolver } from './posts.resolver.js'; +import { PostsService } from './posts.service.js'; const mockPost: Post = { authorId: 1, @@ -10,8 +10,8 @@ const mockPost: Post = { }; const postsServiceMock = { - findOne: jest.fn((id: number): Post => mockPost), - findAll: jest.fn((): Post[] => [mockPost]), + findOne: vi.fn((id: number): Post => mockPost), + findAll: vi.fn((): Post[] => [mockPost]), }; describe('PostsResolver', () => { diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.ts index c66f8dc38a0..78ed7e469dd 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.resolver.ts @@ -6,9 +6,9 @@ import { ResolveField, Resolver, } from '@nestjs/graphql'; -import { Post } from './models/post.model'; -import { User } from './models/user.model'; -import { PostsService } from './posts.service'; +import { Post } from './models/post.model.js'; +import { User } from './models/user.model.js'; +import { PostsService } from './posts.service.js'; import { ParseIntPipe } from '@nestjs/common'; @Resolver((of) => Post) diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.spec.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.spec.ts index 2157d12c1f0..964bb3ddb19 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.spec.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { PostsService } from './posts.service'; +import { PostsService } from './posts.service.js'; describe('PostsService', () => { let service: PostsService; diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.ts index 90dd4aecff8..e17ac699a82 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/posts.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Post } from './models/post.model'; +import { Post } from './models/post.model.js'; @Injectable() export class PostsService { diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.spec.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.spec.ts index fb05aa12e40..f944bab010e 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.spec.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.spec.ts @@ -1,10 +1,10 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Post } from './models/post.model'; -import { PostsService } from './posts.service'; -import { UsersResolver } from './users.resolver'; +import { Post } from './models/post.model.js'; +import { PostsService } from './posts.service.js'; +import { UsersResolver } from './users.resolver.js'; const postsServiceMock = { - findAllByAuthorId: jest.fn((authorId: number): Post[] => { + findAllByAuthorId: vi.fn((authorId: number): Post[] => { return [{ authorId, id: 1, title: 'Post Mock' }]; }), }; diff --git a/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.ts b/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.ts index 9460e55f34f..6b16d661404 100644 --- a/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.ts +++ b/sample/31-graphql-federation-code-first/posts-application/src/posts/users.resolver.ts @@ -1,7 +1,7 @@ import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { Post } from './models/post.model'; -import { User } from './models/user.model'; -import { PostsService } from './posts.service'; +import { Post } from './models/post.model.js'; +import { User } from './models/user.model.js'; +import { PostsService } from './posts.service.js'; @Resolver((of) => User) export class UsersResolver { diff --git a/sample/31-graphql-federation-code-first/posts-application/tsconfig.json b/sample/31-graphql-federation-code-first/posts-application/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/31-graphql-federation-code-first/posts-application/tsconfig.json +++ b/sample/31-graphql-federation-code-first/posts-application/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/31-graphql-federation-code-first/posts-application/vitest.config.mts b/sample/31-graphql-federation-code-first/posts-application/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/31-graphql-federation-code-first/posts-application/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/31-graphql-federation-code-first/users-application/eslint.config.mjs b/sample/31-graphql-federation-code-first/users-application/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/31-graphql-federation-code-first/users-application/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/31-graphql-federation-code-first/users-application/package.json b/sample/31-graphql-federation-code-first/users-application/package.json index 659cacfa646..53ff8a65a89 100644 --- a/sample/31-graphql-federation-code-first/users-application/package.json +++ b/sample/31-graphql-federation-code-first/users-application/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/gateway": "2.13.0", @@ -35,45 +35,21 @@ "ts-morph": "27.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", "typescript": "5.9.3", - "webpack": "5.105.2", - "typescript-eslint": "8.55.0" + "vitest": "4.1.2", + "webpack": "5.105.0" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/31-graphql-federation-code-first/users-application/src/app.module.ts b/sample/31-graphql-federation-code-first/users-application/src/app.module.ts index ab204589170..d739e1c5416 100644 --- a/sample/31-graphql-federation-code-first/users-application/src/app.module.ts +++ b/sample/31-graphql-federation-code-first/users-application/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { UsersModule } from './users/users.module'; +import { UsersModule } from './users/users.module.js'; @Module({ imports: [UsersModule], diff --git a/sample/31-graphql-federation-code-first/users-application/src/main.ts b/sample/31-graphql-federation-code-first/users-application/src/main.ts index cdebcd75e5c..31d38415014 100644 --- a/sample/31-graphql-federation-code-first/users-application/src/main.ts +++ b/sample/31-graphql-federation-code-first/users-application/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3002); } -bootstrap(); +await bootstrap(); diff --git a/sample/31-graphql-federation-code-first/users-application/src/users/users.module.ts b/sample/31-graphql-federation-code-first/users-application/src/users/users.module.ts index be992a5e99c..e38717cf7e3 100644 --- a/sample/31-graphql-federation-code-first/users-application/src/users/users.module.ts +++ b/sample/31-graphql-federation-code-first/users-application/src/users/users.module.ts @@ -5,8 +5,8 @@ import { } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; -import { UsersResolver } from './users.resolver'; -import { UsersService } from './users.service'; +import { UsersResolver } from './users.resolver.js'; +import { UsersService } from './users.service.js'; @Module({ providers: [UsersResolver, UsersService], @@ -16,7 +16,7 @@ import { UsersService } from './users.service'; autoSchemaFile: { federation: 2, }, - plugins: [ApolloServerPluginInlineTrace()], + plugins: [ApolloServerPluginInlineTrace() as any], }), ], }) diff --git a/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.spec.ts b/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.spec.ts index 69151d1de68..b663fea8e86 100644 --- a/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.spec.ts +++ b/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.spec.ts @@ -1,10 +1,10 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { User } from './models/user.model'; -import { UsersResolver } from './users.resolver'; -import { UsersService } from './users.service'; +import { User } from './models/user.model.js'; +import { UsersResolver } from './users.resolver.js'; +import { UsersService } from './users.service.js'; const usersServiceMock = { - findById: jest.fn((id: number): User => { + findById: vi.fn((id: number): User => { return { id, name: 'Mocked User' }; }), }; diff --git a/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.ts b/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.ts index eab6f88315d..c68205d1527 100644 --- a/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.ts +++ b/sample/31-graphql-federation-code-first/users-application/src/users/users.resolver.ts @@ -1,6 +1,6 @@ import { Args, ID, Query, Resolver, ResolveReference } from '@nestjs/graphql'; -import { User } from './models/user.model'; -import { UsersService } from './users.service'; +import { User } from './models/user.model.js'; +import { UsersService } from './users.service.js'; @Resolver((of) => User) export class UsersResolver { diff --git a/sample/31-graphql-federation-code-first/users-application/src/users/users.service.spec.ts b/sample/31-graphql-federation-code-first/users-application/src/users/users.service.spec.ts index f59f86f48a2..10bd18ae1d0 100644 --- a/sample/31-graphql-federation-code-first/users-application/src/users/users.service.spec.ts +++ b/sample/31-graphql-federation-code-first/users-application/src/users/users.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; describe('UsersService', () => { let service: UsersService; diff --git a/sample/31-graphql-federation-code-first/users-application/src/users/users.service.ts b/sample/31-graphql-federation-code-first/users-application/src/users/users.service.ts index 1b3c1699358..c5a88677c6b 100644 --- a/sample/31-graphql-federation-code-first/users-application/src/users/users.service.ts +++ b/sample/31-graphql-federation-code-first/users-application/src/users/users.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { User } from './models/user.model'; +import { User } from './models/user.model.js'; @Injectable() export class UsersService { diff --git a/sample/31-graphql-federation-code-first/users-application/tsconfig.json b/sample/31-graphql-federation-code-first/users-application/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/31-graphql-federation-code-first/users-application/tsconfig.json +++ b/sample/31-graphql-federation-code-first/users-application/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/31-graphql-federation-code-first/users-application/vitest.config.mts b/sample/31-graphql-federation-code-first/users-application/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/31-graphql-federation-code-first/users-application/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/32-graphql-federation-schema-first/README.md b/sample/32-graphql-federation-schema-first/README.md index 7a0f47a9d98..b0d95e90f38 100644 --- a/sample/32-graphql-federation-schema-first/README.md +++ b/sample/32-graphql-federation-schema-first/README.md @@ -1,12 +1,25 @@ # GraphQL Federation - Schema First -A simple example of GraphQL Federation using Schema First approach. +A production-ready example of GraphQL Federation using Schema First approach. + +## Installation + +Install dependencies for all applications: + +```sh +cd users-application && npm install +cd ../posts-application && npm install +cd ../gateway && npm install +``` ## Execution -Make sure to start the two sub-graph applications first, then the gateway. Otherwise the gateway won't be able to fetch schemas from the sub-graphs. +### Development Mode + +For development, the gateway will automatically generate a supergraph schema from local schema files: ```sh +# Start sub-graph applications first cd users-application && npm run start ``` @@ -15,9 +28,46 @@ cd posts-application && npm run start ``` ```sh +# The gateway will auto-generate supergraph.graphql and TypeScript types on startup cd gateway && npm run start ``` +### Production Mode + +For production, generate the supergraph schema and types beforehand: + +```sh +# 1. Ensure sub-graphs are running (for Rover CLI method) +cd users-application && npm run start +cd ../posts-application && npm run start + +# 2. Generate supergraph schema using Rover CLI (requires @apollo/rover installation) +cd ../gateway && npm run generate:supergraph:rover + +# 3. Generate TypeScript types from supergraph +npm run generate:typings + +# 4. Start the gateway with the static supergraph +npm run start:prod +``` + +## Schema Generation + +The gateway provides multiple generation scripts: + +1. **Supergraph Generation (Local)**: `npm run generate:supergraph` + - Composes supergraph from local `.graphql` files + - Suitable for development when sub-graphs share the same repository + +2. **Supergraph Generation (Rover)**: `npm run generate:supergraph:rover` + - Uses Apollo Rover to compose supergraph from running sub-graphs + - Requires Rover CLI: `npm install -g @apollo/rover` + - Recommended for production deployments + +3. **TypeScript Types Generation**: `npm run generate:typings` + - Generates TypeScript interfaces from the supergraph schema + - Provides type safety for resolvers and GraphQL operations + ## Access the graph You can reach the gateway under `http://localhost:3002/graphql` diff --git a/sample/32-graphql-federation-schema-first/gateway/.gitignore b/sample/32-graphql-federation-schema-first/gateway/.gitignore index c16ef026a31..070fcda8812 100644 --- a/sample/32-graphql-federation-schema-first/gateway/.gitignore +++ b/sample/32-graphql-federation-schema-first/gateway/.gitignore @@ -31,4 +31,8 @@ lerna-debug.log* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file +!.vscode/extensions.json + +# Generated files +supergraph.graphql +src/graphql.schema.ts diff --git a/sample/32-graphql-federation-schema-first/gateway/eslint.config.mjs b/sample/32-graphql-federation-schema-first/gateway/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/32-graphql-federation-schema-first/gateway/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/32-graphql-federation-schema-first/gateway/generate-supergraph.ts b/sample/32-graphql-federation-schema-first/gateway/generate-supergraph.ts new file mode 100644 index 00000000000..0e1e9016b70 --- /dev/null +++ b/sample/32-graphql-federation-schema-first/gateway/generate-supergraph.ts @@ -0,0 +1,132 @@ +import { exec } from 'node:child_process'; +import { promisify } from 'node:util'; +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const execAsync = promisify(exec); +const __dirname = dirname(fileURLToPath(import.meta.url)); + +async function generateSupergraph() { + try { + try { + await execAsync('rover --version'); + } catch { + console.error('Rover CLI is not installed. Please install it first:'); + console.error('npm install -g @apollo/rover'); + process.exit(1); + } + + console.log('Generating supergraph schema...'); + const { stdout, stderr } = await execAsync( + 'rover supergraph compose --config ./supergraph.yaml', + ); + + if (stderr && !stderr.includes('WARN')) { + console.error('Error generating supergraph:', stderr); + process.exit(1); + } + + writeFileSync(join(__dirname, 'supergraph.graphql'), stdout); + console.log('Supergraph schema generated successfully!'); + console.log('Output written to: supergraph.graphql'); + } catch (error) { + console.error('Failed to generate supergraph:', error); + process.exit(1); + } +} + +async function generateSupergraphLocal() { + console.log('Generating local supergraph schema from schema files...'); + + const usersSchemaPath = join( + __dirname, + '..', + 'users-application', + 'src', + 'users', + 'users.graphql', + ); + const postsSchemaPath = join( + __dirname, + '..', + 'posts-application', + 'src', + 'posts', + 'posts.graphql', + ); + + if (!existsSync(usersSchemaPath) || !existsSync(postsSchemaPath)) { + console.error( + 'Schema files not found. Make sure both sub-graph applications have their schema files.', + ); + process.exit(1); + } + + const usersSchema = readFileSync(usersSchemaPath, 'utf-8'); + const postsSchema = readFileSync(postsSchemaPath, 'utf-8'); + + const supergraphSchema = `\ +schema + @link(url: "https://specs.apollo.dev/federation/v2.3") + @link(url: "https://specs.apollo.dev/link/v1.0") +{ + query: Query +} + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +directive @key(fields: String!, resolvable: Boolean = true) on OBJECT | INTERFACE + +directive @requires(fields: String!) on FIELD_DEFINITION + +directive @provides(fields: String!) on FIELD_DEFINITION + +directive @external on OBJECT | FIELD_DEFINITION + +directive @shareable on OBJECT | FIELD_DEFINITION + +directive @extends on OBJECT | INTERFACE + +scalar link__Import + +enum link__Purpose { + SECURITY + EXECUTION +} + +type Query { + _entities(representations: [_Any!]!): [_Entity]! + _service: _Service! + getUser(id: ID!): User + getPosts: [Post] + findPost(id: ID!): Post +} + +union _Entity = User | Post + +scalar _Any + +type _Service { + sdl: String! +} + +${usersSchema.replace(/^extend type Query[\s\S]*?}$/m, '')} + +${postsSchema.replace(/^extend type Query[\s\S]*?}$/m, '').replace(/^extend type User[\s\S]*?}$/m, '')} +`; + + writeFileSync(join(__dirname, 'supergraph.graphql'), supergraphSchema); + console.log('Local supergraph schema generated successfully!'); + console.log( + 'Note: For production, use Rover CLI with actual running sub-graphs.', + ); +} + +const useRover = process.argv.includes('--rover'); + +if (useRover) { + await generateSupergraph(); +} else { + await generateSupergraphLocal(); +} diff --git a/sample/32-graphql-federation-schema-first/gateway/generate-typings.ts b/sample/32-graphql-federation-schema-first/gateway/generate-typings.ts new file mode 100644 index 00000000000..1b9bb8c011d --- /dev/null +++ b/sample/32-graphql-federation-schema-first/gateway/generate-typings.ts @@ -0,0 +1,11 @@ +import { GraphQLDefinitionsFactory } from '@nestjs/graphql'; +import { join } from 'node:path'; + +const definitionsFactory = new GraphQLDefinitionsFactory(); +await definitionsFactory.generate({ + typePaths: ['./supergraph.graphql'], + path: join(process.cwd(), 'src/graphql.schema.ts'), + outputAs: 'interface', + skipResolverArgs: true, + federation: true, +}); diff --git a/sample/32-graphql-federation-schema-first/gateway/package.json b/sample/32-graphql-federation-schema-first/gateway/package.json index a4cfc8558ed..ba33568d4e5 100644 --- a/sample/32-graphql-federation-schema-first/gateway/package.json +++ b/sample/32-graphql-federation-schema-first/gateway/package.json @@ -13,12 +13,17 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "generate:supergraph": "ts-node --esm generate-supergraph.ts", + "generate:supergraph:rover": "ts-node --esm generate-supergraph.ts --rover", + "generate:typings": "ts-node --esm generate-typings.ts", + "prestart": "npm run generate:supergraph && npm run generate:typings", + "prestart:dev": "npm run generate:supergraph && npm run generate:typings", + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/gateway": "2.13.0", @@ -36,44 +41,20 @@ "ts-morph": "27.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/32-graphql-federation-schema-first/gateway/src/app.module.ts b/sample/32-graphql-federation-schema-first/gateway/src/app.module.ts index 0ee8b303dde..844dc4a7c01 100644 --- a/sample/32-graphql-federation-schema-first/gateway/src/app.module.ts +++ b/sample/32-graphql-federation-schema-first/gateway/src/app.module.ts @@ -1,19 +1,23 @@ -import { IntrospectAndCompose } from '@apollo/gateway'; import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const supergraphSdl = readFileSync( + join(__dirname, '..', 'supergraph.graphql'), + 'utf-8', +).trim(); @Module({ imports: [ GraphQLModule.forRoot({ driver: ApolloGatewayDriver, gateway: { - supergraphSdl: new IntrospectAndCompose({ - subgraphs: [ - { name: 'users', url: 'http://localhost:3000/graphql' }, - { name: 'posts', url: 'http://localhost:3001/graphql' }, - ], - }), + supergraphSdl, }, }), ], diff --git a/sample/32-graphql-federation-schema-first/gateway/src/main.ts b/sample/32-graphql-federation-schema-first/gateway/src/main.ts index cdebcd75e5c..31d38415014 100644 --- a/sample/32-graphql-federation-schema-first/gateway/src/main.ts +++ b/sample/32-graphql-federation-schema-first/gateway/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3002); } -bootstrap(); +await bootstrap(); diff --git a/sample/32-graphql-federation-schema-first/gateway/supergraph.yaml b/sample/32-graphql-federation-schema-first/gateway/supergraph.yaml new file mode 100644 index 00000000000..136b1eb7049 --- /dev/null +++ b/sample/32-graphql-federation-schema-first/gateway/supergraph.yaml @@ -0,0 +1,10 @@ +federation_version: =2.3.2 +subgraphs: + users: + routing_url: http://localhost:3000/graphql + schema: + file: ../users-application/src/users/users.graphql + posts: + routing_url: http://localhost:3001/graphql + schema: + file: ../posts-application/src/posts/posts.graphql diff --git a/sample/32-graphql-federation-schema-first/gateway/test/app.e2e-spec.ts b/sample/32-graphql-federation-schema-first/gateway/test/app.e2e-spec.ts new file mode 100644 index 00000000000..7799cebe828 --- /dev/null +++ b/sample/32-graphql-federation-schema-first/gateway/test/app.e2e-spec.ts @@ -0,0 +1,80 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from '../src/app.module.js'; +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +describe('GraphQL Federation Gateway (Schema First)', () => { + let app: INestApplication; + + beforeAll(async () => { + const supergraphPath = join(__dirname, '..', 'supergraph.graphql'); + if (!existsSync(supergraphPath)) { + await import('../generate-supergraph.js'); + } + + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should load gateway with static supergraph', () => { + return request(app.getHttpServer()) + .post('/graphql') + .send({ + query: ` + query { + _service { + sdl + } + } + `, + }) + .expect(200) + .expect((res) => { + expect(res.body.data).toBeDefined(); + expect(res.body.data._service).toBeDefined(); + expect(res.body.data._service.sdl).toContain('type User'); + expect(res.body.data._service.sdl).toContain('type Post'); + }); + }); + + it('should not use IntrospectAndCompose in production', () => { + const appModulePath = join(__dirname, '..', 'src', 'app.module.ts'); + const appModuleContent = readFileSync(appModulePath, 'utf-8'); + + const hasActiveIntrospect = + appModuleContent.includes('new IntrospectAndCompose') && + !appModuleContent.includes('// supergraphSdl: new IntrospectAndCompose'); + + expect(hasActiveIntrospect).toBe(false); + }); + + it('should read supergraph from file system', () => { + const appModulePath = join(__dirname, '..', 'src', 'app.module.ts'); + const appModuleContent = readFileSync(appModulePath, 'utf-8'); + + expect(appModuleContent).toContain('readFileSync'); + expect(appModuleContent).toContain('supergraph.graphql'); + }); + + it('should have schema generation scripts configured', () => { + const packageJsonPath = join(__dirname, '..', 'package.json'); + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); + + expect(packageJson.scripts['generate:supergraph']).toBeDefined(); + expect(packageJson.scripts['generate:supergraph:rover']).toBeDefined(); + expect(packageJson.scripts['generate:typings']).toBeDefined(); + }); +}); diff --git a/sample/32-graphql-federation-schema-first/gateway/tsconfig.json b/sample/32-graphql-federation-schema-first/gateway/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/32-graphql-federation-schema-first/gateway/tsconfig.json +++ b/sample/32-graphql-federation-schema-first/gateway/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/32-graphql-federation-schema-first/gateway/vitest.config.e2e.mts b/sample/32-graphql-federation-schema-first/gateway/vitest.config.e2e.mts new file mode 100644 index 00000000000..c13699cd867 --- /dev/null +++ b/sample/32-graphql-federation-schema-first/gateway/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['test/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/32-graphql-federation-schema-first/gateway/vitest.config.mts b/sample/32-graphql-federation-schema-first/gateway/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/32-graphql-federation-schema-first/gateway/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/32-graphql-federation-schema-first/posts-application/eslint.config.mjs b/sample/32-graphql-federation-schema-first/posts-application/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/32-graphql-federation-schema-first/posts-application/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/32-graphql-federation-schema-first/posts-application/package.json b/sample/32-graphql-federation-schema-first/posts-application/package.json index 11d887c7c86..fe91088418e 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/package.json +++ b/sample/32-graphql-federation-schema-first/posts-application/package.json @@ -13,12 +13,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/gateway": "2.13.0", @@ -37,44 +37,20 @@ "ts-morph": "27.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/app.module.ts b/sample/32-graphql-federation-schema-first/posts-application/src/app.module.ts index ec8d38a4596..0a8aa8e2afc 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/app.module.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { PostsModule } from './posts/posts.module'; +import { PostsModule } from './posts/posts.module.js'; @Module({ imports: [PostsModule], diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/main.ts b/sample/32-graphql-federation-schema-first/posts-application/src/main.ts index 7d4c1629f4b..178a3e241da 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/main.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3001); } -bootstrap(); +await bootstrap(); diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/post.model.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/post.model.ts index 0962b84c312..90d75b25e53 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/post.model.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/post.model.ts @@ -1,5 +1,5 @@ import { Directive, Field, ID, Int, ObjectType } from '@nestjs/graphql'; -import { User } from './user.model'; +import { User } from './user.model.js'; @ObjectType() @Directive('@key(fields: "id")') diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/user.model.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/user.model.ts index 492ea1c8565..8b148cd470e 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/user.model.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/models/user.model.ts @@ -1,5 +1,5 @@ import { Directive, Field, ID, ObjectType } from '@nestjs/graphql'; -import { Post } from './post.model'; +import { Post } from './post.model.js'; @ObjectType() @Directive('@extends') diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.module.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.module.ts index 2254d715db6..5774ab8f575 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.module.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.module.ts @@ -4,9 +4,9 @@ import { } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; -import { PostsResolver } from './posts.resolver'; -import { PostsService } from './posts.service'; -import { UsersResolver } from './users.resolver'; +import { PostsResolver } from './posts.resolver.js'; +import { PostsService } from './posts.service.js'; +import { UsersResolver } from './users.resolver.js'; @Module({ imports: [ diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.spec.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.spec.ts index 77c80b38f2b..8e5640fe564 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.spec.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Post } from './models/post.model'; -import { PostsResolver } from './posts.resolver'; -import { PostsService } from './posts.service'; +import { Post } from './models/post.model.js'; +import { PostsResolver } from './posts.resolver.js'; +import { PostsService } from './posts.service.js'; const mockPost: Post = { authorId: 1, @@ -10,8 +10,8 @@ const mockPost: Post = { }; const postsServiceMock = { - findOne: jest.fn((id: number): Post => mockPost), - findAll: jest.fn((): Post[] => [mockPost]), + findOne: vi.fn((id: number): Post => mockPost), + findAll: vi.fn((): Post[] => [mockPost]), }; describe('PostsResolver', () => { diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.ts index 60fe11276d2..ebbd7f65680 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.resolver.ts @@ -6,8 +6,8 @@ import { ResolveField, Resolver, } from '@nestjs/graphql'; -import { Post } from './posts.interfaces'; -import { PostsService } from './posts.service'; +import { Post } from './posts.interfaces.js'; +import { PostsService } from './posts.service.js'; @Resolver('Post') export class PostsResolver { diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.spec.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.spec.ts index 27eb75027f7..8ea610f5c94 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.spec.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { PostsService } from './posts.service'; +import { PostsService } from './posts.service.js'; describe('PostsService', () => { let service: PostsService; diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.ts index ab7620ff992..f4f594b37b6 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/posts.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Post } from './posts.interfaces'; +import { Post } from './posts.interfaces.js'; @Injectable() export class PostsService { diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.interfaces.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.interfaces.ts index e936063392d..8b32b09132a 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.interfaces.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.interfaces.ts @@ -1,4 +1,4 @@ -import { Post } from './posts.interfaces'; +import { Post } from './posts.interfaces.js'; export interface User { id: number; diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.spec.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.spec.ts index 42936db1f4f..c76413e6846 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.spec.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.spec.ts @@ -1,10 +1,10 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Post } from './models/post.model'; -import { PostsService } from './posts.service'; -import { UsersResolver } from './users.resolver'; +import { Post } from './models/post.model.js'; +import { PostsService } from './posts.service.js'; +import { UsersResolver } from './users.resolver.js'; const postsServiceMock = { - findOneByAuthorId: jest.fn((authorId: number): Post[] => { + findOneByAuthorId: vi.fn((authorId: number): Post[] => { return [{ authorId, id: 1, title: 'Post Mock' }]; }), }; diff --git a/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.ts b/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.ts index b591e40b684..6a359194ceb 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.ts +++ b/sample/32-graphql-federation-schema-first/posts-application/src/posts/users.resolver.ts @@ -1,6 +1,6 @@ import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; -import { PostsService } from './posts.service'; -import { User } from './users.interfaces'; +import { PostsService } from './posts.service.js'; +import { User } from './users.interfaces.js'; @Resolver('User') export class UsersResolver { diff --git a/sample/32-graphql-federation-schema-first/posts-application/tsconfig.json b/sample/32-graphql-federation-schema-first/posts-application/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/32-graphql-federation-schema-first/posts-application/tsconfig.json +++ b/sample/32-graphql-federation-schema-first/posts-application/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/32-graphql-federation-schema-first/posts-application/vitest.config.mts b/sample/32-graphql-federation-schema-first/posts-application/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/32-graphql-federation-schema-first/posts-application/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/32-graphql-federation-schema-first/users-application/eslint.config.mjs b/sample/32-graphql-federation-schema-first/users-application/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/32-graphql-federation-schema-first/users-application/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/32-graphql-federation-schema-first/users-application/package.json b/sample/32-graphql-federation-schema-first/users-application/package.json index 10825547772..2e429b066ac 100644 --- a/sample/32-graphql-federation-schema-first/users-application/package.json +++ b/sample/32-graphql-federation-schema-first/users-application/package.json @@ -12,12 +12,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@apollo/gateway": "2.13.0", @@ -36,43 +36,20 @@ "ts-morph": "27.0.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.5", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", "typescript": "5.9.3", - "typescript-eslint": "8.55.0" + "vitest": "4.1.2" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } + "type": "module" } diff --git a/sample/32-graphql-federation-schema-first/users-application/src/app.module.ts b/sample/32-graphql-federation-schema-first/users-application/src/app.module.ts index ab204589170..d739e1c5416 100644 --- a/sample/32-graphql-federation-schema-first/users-application/src/app.module.ts +++ b/sample/32-graphql-federation-schema-first/users-application/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { UsersModule } from './users/users.module'; +import { UsersModule } from './users/users.module.js'; @Module({ imports: [UsersModule], diff --git a/sample/32-graphql-federation-schema-first/users-application/src/main.ts b/sample/32-graphql-federation-schema-first/users-application/src/main.ts index 13cad38cff9..524db419b62 100644 --- a/sample/32-graphql-federation-schema-first/users-application/src/main.ts +++ b/sample/32-graphql-federation-schema-first/users-application/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } -bootstrap(); +await bootstrap(); diff --git a/sample/32-graphql-federation-schema-first/users-application/src/users/users.module.ts b/sample/32-graphql-federation-schema-first/users-application/src/users/users.module.ts index d98abacac9e..e51b094767c 100644 --- a/sample/32-graphql-federation-schema-first/users-application/src/users/users.module.ts +++ b/sample/32-graphql-federation-schema-first/users-application/src/users/users.module.ts @@ -4,8 +4,8 @@ import { } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; -import { UsersResolver } from './users.resolver'; -import { UsersService } from './users.service'; +import { UsersResolver } from './users.resolver.js'; +import { UsersService } from './users.service.js'; @Module({ providers: [UsersResolver, UsersService], diff --git a/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.spec.ts b/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.spec.ts index 69151d1de68..b663fea8e86 100644 --- a/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.spec.ts +++ b/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.spec.ts @@ -1,10 +1,10 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { User } from './models/user.model'; -import { UsersResolver } from './users.resolver'; -import { UsersService } from './users.service'; +import { User } from './models/user.model.js'; +import { UsersResolver } from './users.resolver.js'; +import { UsersService } from './users.service.js'; const usersServiceMock = { - findById: jest.fn((id: number): User => { + findById: vi.fn((id: number): User => { return { id, name: 'Mocked User' }; }), }; diff --git a/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.ts b/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.ts index 7093e08c8b1..6f80e669ca2 100644 --- a/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.ts +++ b/sample/32-graphql-federation-schema-first/users-application/src/users/users.resolver.ts @@ -1,5 +1,5 @@ import { Args, ID, Query, Resolver, ResolveReference } from '@nestjs/graphql'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; @Resolver('User') export class UsersResolver { diff --git a/sample/32-graphql-federation-schema-first/users-application/src/users/users.service.spec.ts b/sample/32-graphql-federation-schema-first/users-application/src/users/users.service.spec.ts index f59f86f48a2..10bd18ae1d0 100644 --- a/sample/32-graphql-federation-schema-first/users-application/src/users/users.service.spec.ts +++ b/sample/32-graphql-federation-schema-first/users-application/src/users/users.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { UsersService } from './users.service'; +import { UsersService } from './users.service.js'; describe('UsersService', () => { let service: UsersService; diff --git a/sample/32-graphql-federation-schema-first/users-application/tsconfig.json b/sample/32-graphql-federation-schema-first/users-application/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/32-graphql-federation-schema-first/users-application/tsconfig.json +++ b/sample/32-graphql-federation-schema-first/users-application/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/32-graphql-federation-schema-first/users-application/vitest.config.mts b/sample/32-graphql-federation-schema-first/users-application/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/32-graphql-federation-schema-first/users-application/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/33-graphql-mercurius/e2e/recipes/recipes.e2e-spec.ts b/sample/33-graphql-mercurius/e2e/recipes/recipes.e2e-spec.ts new file mode 100644 index 00000000000..6fdf643a588 --- /dev/null +++ b/sample/33-graphql-mercurius/e2e/recipes/recipes.e2e-spec.ts @@ -0,0 +1,61 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { + FastifyAdapter, + NestFastifyApplication, +} from '@nestjs/platform-fastify'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('RecipesResolver (e2e)', () => { + let app: NestFastifyApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication( + new FastifyAdapter(), + ); + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('should return an empty list of recipes', async () => { + const query = ` + query { + recipes { + id + title + description + } + } + `; + + const response = await request(app.getHttpServer()) + .post('/graphql') + .send({ query }) + .expect(200); + + expect(response.body.data.recipes).toEqual([]); + }); + + it('should remove a recipe', async () => { + const query = ` + mutation { + removeRecipe(id: "1") + } + `; + + const response = await request(app.getHttpServer()) + .post('/graphql') + .send({ query }) + .expect(200); + + expect(response.body.data.removeRecipe).toBe(true); + }); +}); diff --git a/sample/33-graphql-mercurius/eslint.config.mjs b/sample/33-graphql-mercurius/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/33-graphql-mercurius/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/33-graphql-mercurius/package.json b/sample/33-graphql-mercurius/package.json index 9ca613d87d7..4553adf4d86 100644 --- a/sample/33-graphql-mercurius/package.json +++ b/sample/33-graphql-mercurius/package.json @@ -11,12 +11,12 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "echo 'No e2e tests implemented yet.'" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -32,26 +32,21 @@ "rxjs": "7.8.2" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", "@types/node": "24.10.13", "@types/supertest": "6.0.3", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", "rimraf": "6.1.2", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - } + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" } diff --git a/sample/33-graphql-mercurius/src/app.module.ts b/sample/33-graphql-mercurius/src/app.module.ts index 53cc6458e27..22eaf2abb0f 100644 --- a/sample/33-graphql-mercurius/src/app.module.ts +++ b/sample/33-graphql-mercurius/src/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { MercuriusDriver, MercuriusDriverConfig } from '@nestjs/mercurius'; -import { RecipesModule } from './recipes/recipes.module'; +import { RecipesModule } from './recipes/recipes.module.js'; @Module({ imports: [ diff --git a/sample/33-graphql-mercurius/src/main.ts b/sample/33-graphql-mercurius/src/main.ts index 8e788666710..9c35f933366 100644 --- a/sample/33-graphql-mercurius/src/main.ts +++ b/sample/33-graphql-mercurius/src/main.ts @@ -1,7 +1,7 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { FastifyAdapter } from '@nestjs/platform-fastify'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule, new FastifyAdapter()); @@ -10,4 +10,4 @@ async function bootstrap() { await app.listen(3000); console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/33-graphql-mercurius/src/recipes/recipes.module.ts b/sample/33-graphql-mercurius/src/recipes/recipes.module.ts index fd2d2cc95f6..e3b3c691fe3 100644 --- a/sample/33-graphql-mercurius/src/recipes/recipes.module.ts +++ b/sample/33-graphql-mercurius/src/recipes/recipes.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { DateScalar } from '../common/scalars/date.scalar'; -import { RecipesResolver } from './recipes.resolver'; -import { RecipesService } from './recipes.service'; +import { DateScalar } from '../common/scalars/date.scalar.js'; +import { RecipesResolver } from './recipes.resolver.js'; +import { RecipesService } from './recipes.service.js'; @Module({ providers: [RecipesResolver, RecipesService, DateScalar], diff --git a/sample/33-graphql-mercurius/src/recipes/recipes.resolver.ts b/sample/33-graphql-mercurius/src/recipes/recipes.resolver.ts index 5b42038fcfc..a2e906a882d 100644 --- a/sample/33-graphql-mercurius/src/recipes/recipes.resolver.ts +++ b/sample/33-graphql-mercurius/src/recipes/recipes.resolver.ts @@ -8,10 +8,10 @@ import { Subscription, } from '@nestjs/graphql'; import { PubSub } from 'mercurius'; -import { NewRecipeInput } from './dto/new-recipe.input'; -import { RecipesArgs } from './dto/recipes.args'; -import { Recipe } from './models/recipe.model'; -import { RecipesService } from './recipes.service'; +import { NewRecipeInput } from './dto/new-recipe.input.js'; +import { RecipesArgs } from './dto/recipes.args.js'; +import { Recipe } from './models/recipe.model.js'; +import { RecipesService } from './recipes.service.js'; @Resolver(of => Recipe) export class RecipesResolver { diff --git a/sample/33-graphql-mercurius/src/recipes/recipes.service.ts b/sample/33-graphql-mercurius/src/recipes/recipes.service.ts index a4d6428eb55..ea609171792 100644 --- a/sample/33-graphql-mercurius/src/recipes/recipes.service.ts +++ b/sample/33-graphql-mercurius/src/recipes/recipes.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { NewRecipeInput } from './dto/new-recipe.input'; -import { RecipesArgs } from './dto/recipes.args'; -import { Recipe } from './models/recipe.model'; +import { NewRecipeInput } from './dto/new-recipe.input.js'; +import { RecipesArgs } from './dto/recipes.args.js'; +import { Recipe } from './models/recipe.model.js'; @Injectable() export class RecipesService { diff --git a/sample/33-graphql-mercurius/tsconfig.json b/sample/33-graphql-mercurius/tsconfig.json index d9c82ca9758..5b7b4fc7d6f 100644 --- a/sample/33-graphql-mercurius/tsconfig.json +++ b/sample/33-graphql-mercurius/tsconfig.json @@ -1,17 +1,24 @@ { "compilerOptions": { - "module": "commonjs", + "module": "nodenext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "outDir": "./dist", - "baseUrl": "./", "incremental": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false }, - "include": ["src/**/*"] + "include": [ + "src/**/*" + ] } diff --git a/sample/33-graphql-mercurius/vitest.config.e2e.mts b/sample/33-graphql-mercurius/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/33-graphql-mercurius/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/33-graphql-mercurius/vitest.config.mts b/sample/33-graphql-mercurius/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/33-graphql-mercurius/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/36-hmr-esm/.gitignore b/sample/34-hmr-esm/.gitignore similarity index 100% rename from sample/36-hmr-esm/.gitignore rename to sample/34-hmr-esm/.gitignore diff --git a/sample/36-hmr-esm/e2e/cats/cats.e2e-spec.ts b/sample/34-hmr-esm/e2e/cats/cats.e2e-spec.ts similarity index 76% rename from sample/36-hmr-esm/e2e/cats/cats.e2e-spec.ts rename to sample/34-hmr-esm/e2e/cats/cats.e2e-spec.ts index 0baf3b62045..79b91ca2226 100644 --- a/sample/36-hmr-esm/e2e/cats/cats.e2e-spec.ts +++ b/sample/34-hmr-esm/e2e/cats/cats.e2e-spec.ts @@ -1,9 +1,9 @@ import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import * as request from 'supertest'; -import { CatsModule } from '../../src/cats/cats.module'; -import { CatsService } from '../../src/cats/cats.service'; -import { CoreModule } from '../../src/core/core.module'; +import request from 'supertest'; +import { CatsModule } from '../../src/cats/cats.module.js'; +import { CatsService } from '../../src/cats/cats.service.js'; +import { CoreModule } from '../../src/core/core.module.js'; describe('Cats', () => { const catsService = { findAll: () => ['test'] }; diff --git a/sample/36-hmr-esm/jest.json b/sample/34-hmr-esm/jest.json similarity index 100% rename from sample/36-hmr-esm/jest.json rename to sample/34-hmr-esm/jest.json diff --git a/sample/36-hmr-esm/package.json b/sample/34-hmr-esm/package.json similarity index 55% rename from sample/36-hmr-esm/package.json rename to sample/34-hmr-esm/package.json index 64f327ea531..8f34cdb69c6 100644 --- a/sample/36-hmr-esm/package.json +++ b/sample/34-hmr-esm/package.json @@ -13,12 +13,12 @@ "start:debug": "nest start --debug --watch", "start:dev:hmr": "nest build --webpack --webpackPath webpack-hmr.config.cjs --watch", "start:prod": "node dist/main", - "lint": "eslint '{src,apps,libs,test}/**/*.ts' --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./e2e/jest-e2e.json" + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" }, "dependencies": { "@nestjs/common": "11.1.13", @@ -31,47 +31,23 @@ "rxjs": "7.8.2" }, "devDependencies": { - "run-script-webpack-plugin": "0.2.3", - "webpack": "5.105.2", - "webpack-node-externals": "3.0.0", - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.2", "@nestjs/cli": "11.0.16", "@nestjs/schematics": "11.0.9", "@nestjs/testing": "11.1.13", "@types/express": "5.0.6", - "@types/jest": "30.0.0", - "@types/node": "24.10.13", + "@types/node": "24.10.11", "@types/supertest": "6.0.3", "@types/webpack-env": "1.18.8", - "jest": "30.2.0", + "oxlint": "1.58.0", "prettier": "3.8.1", + "run-script-webpack-plugin": "0.2.3", "supertest": "7.2.2", - "ts-jest": "29.4.6", "ts-loader": "9.5.4", "ts-node": "10.9.2", "tsconfig-paths": "4.2.0", - "eslint": "9.39.2", - "eslint-plugin-prettier": "5.5.5", - "globals": "17.3.0", - "typescript": "5.9.3", - "typescript-eslint": "8.55.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" + "typescript": "6.0.2", + "vitest": "4.1.2", + "webpack": "5.105.2", + "webpack-node-externals": "3.0.0" } -} \ No newline at end of file +} diff --git a/sample/36-hmr-esm/src/app.module.ts b/sample/34-hmr-esm/src/app.module.ts similarity index 53% rename from sample/36-hmr-esm/src/app.module.ts rename to sample/34-hmr-esm/src/app.module.ts index 0a635f23081..4b6faa16c13 100644 --- a/sample/36-hmr-esm/src/app.module.ts +++ b/sample/34-hmr-esm/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsModule } from './cats/cats.module'; -import { CoreModule } from './core/core.module'; +import { CatsModule } from './cats/cats.module.js'; +import { CoreModule } from './core/core.module.js'; @Module({ imports: [CoreModule, CatsModule], diff --git a/sample/36-hmr-esm/src/cats/cats.controller.spec.ts b/sample/34-hmr-esm/src/cats/cats.controller.spec.ts similarity index 88% rename from sample/36-hmr-esm/src/cats/cats.controller.spec.ts rename to sample/34-hmr-esm/src/cats/cats.controller.spec.ts index 5eb9ac4f90c..753874846ac 100644 --- a/sample/36-hmr-esm/src/cats/cats.controller.spec.ts +++ b/sample/34-hmr-esm/src/cats/cats.controller.spec.ts @@ -1,7 +1,7 @@ import { Test } from '@nestjs/testing'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; -import { Cat } from './interfaces/cat.interface'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; describe('CatsController', () => { let catsController: CatsController; diff --git a/sample/36-hmr-esm/src/cats/cats.controller.ts b/sample/34-hmr-esm/src/cats/cats.controller.ts similarity index 62% rename from sample/36-hmr-esm/src/cats/cats.controller.ts rename to sample/34-hmr-esm/src/cats/cats.controller.ts index d9df5203327..2d23d36a724 100644 --- a/sample/36-hmr-esm/src/cats/cats.controller.ts +++ b/sample/34-hmr-esm/src/cats/cats.controller.ts @@ -1,10 +1,10 @@ import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common'; -import { Roles } from '../common/decorators/roles.decorator'; -import { RolesGuard } from '../common/guards/roles.guard'; -import { ParseIntPipe } from '../common/pipes/parse-int.pipe'; -import { CatsService } from './cats.service'; -import { CreateCatDto } from './dto/create-cat.dto'; -import { Cat } from './interfaces/cat.interface'; +import { Roles } from '../common/decorators/roles.decorator.js'; +import { RolesGuard } from '../common/guards/roles.guard.js'; +import { ParseIntPipe } from '../common/pipes/parse-int.pipe.js'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; @UseGuards(RolesGuard) @Controller('cats') diff --git a/sample/36-hmr-esm/src/cats/cats.module.ts b/sample/34-hmr-esm/src/cats/cats.module.ts similarity index 57% rename from sample/36-hmr-esm/src/cats/cats.module.ts rename to sample/34-hmr-esm/src/cats/cats.module.ts index f3291c7d11e..14b21cd9f14 100644 --- a/sample/36-hmr-esm/src/cats/cats.module.ts +++ b/sample/34-hmr-esm/src/cats/cats.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { CatsController } from './cats.controller'; -import { CatsService } from './cats.service'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; @Module({ controllers: [CatsController], diff --git a/sample/36-hmr-esm/src/cats/cats.service.spec.ts b/sample/34-hmr-esm/src/cats/cats.service.spec.ts similarity index 91% rename from sample/36-hmr-esm/src/cats/cats.service.spec.ts rename to sample/34-hmr-esm/src/cats/cats.service.spec.ts index cdf8ee02ab1..e5ef4d4662f 100644 --- a/sample/36-hmr-esm/src/cats/cats.service.spec.ts +++ b/sample/34-hmr-esm/src/cats/cats.service.spec.ts @@ -1,6 +1,6 @@ import { Test } from '@nestjs/testing'; -import { CatsService } from './cats.service'; -import { Cat } from './interfaces/cat.interface'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; describe('CatsService', () => { let catsService: CatsService; diff --git a/sample/36-hmr-esm/src/cats/cats.service.ts b/sample/34-hmr-esm/src/cats/cats.service.ts similarity index 82% rename from sample/36-hmr-esm/src/cats/cats.service.ts rename to sample/34-hmr-esm/src/cats/cats.service.ts index 955f564087d..497c33de5a5 100644 --- a/sample/36-hmr-esm/src/cats/cats.service.ts +++ b/sample/34-hmr-esm/src/cats/cats.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Cat } from './interfaces/cat.interface'; +import { Cat } from './interfaces/cat.interface.js'; @Injectable() export class CatsService { diff --git a/sample/36-hmr-esm/src/cats/dto/create-cat.dto.ts b/sample/34-hmr-esm/src/cats/dto/create-cat.dto.ts similarity index 100% rename from sample/36-hmr-esm/src/cats/dto/create-cat.dto.ts rename to sample/34-hmr-esm/src/cats/dto/create-cat.dto.ts diff --git a/sample/36-hmr-esm/src/cats/interfaces/cat.interface.ts b/sample/34-hmr-esm/src/cats/interfaces/cat.interface.ts similarity index 100% rename from sample/36-hmr-esm/src/cats/interfaces/cat.interface.ts rename to sample/34-hmr-esm/src/cats/interfaces/cat.interface.ts diff --git a/sample/36-hmr-esm/src/common/decorators/roles.decorator.ts b/sample/34-hmr-esm/src/common/decorators/roles.decorator.ts similarity index 100% rename from sample/36-hmr-esm/src/common/decorators/roles.decorator.ts rename to sample/34-hmr-esm/src/common/decorators/roles.decorator.ts diff --git a/sample/36-hmr-esm/src/common/filters/http-exception.filter.ts b/sample/34-hmr-esm/src/common/filters/http-exception.filter.ts similarity index 100% rename from sample/36-hmr-esm/src/common/filters/http-exception.filter.ts rename to sample/34-hmr-esm/src/common/filters/http-exception.filter.ts diff --git a/sample/36-hmr-esm/src/common/guards/roles.guard.ts b/sample/34-hmr-esm/src/common/guards/roles.guard.ts similarity index 80% rename from sample/36-hmr-esm/src/common/guards/roles.guard.ts rename to sample/34-hmr-esm/src/common/guards/roles.guard.ts index 59636afa8a2..439d6081967 100644 --- a/sample/36-hmr-esm/src/common/guards/roles.guard.ts +++ b/sample/34-hmr-esm/src/common/guards/roles.guard.ts @@ -1,6 +1,6 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { Roles } from '../decorators/roles.decorator'; +import { Roles } from '../decorators/roles.decorator.js'; @Injectable() export class RolesGuard implements CanActivate { @@ -14,7 +14,7 @@ export class RolesGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const user = request.user; const hasRole = () => - user.roles.some(role => !!roles.find(item => item === role)); + user.roles.some((role: string) => !!roles.find(item => item === role)); return user && user.roles && hasRole(); } diff --git a/sample/36-hmr-esm/src/common/interceptors/exception.interceptor.ts b/sample/34-hmr-esm/src/common/interceptors/exception.interceptor.ts similarity index 100% rename from sample/36-hmr-esm/src/common/interceptors/exception.interceptor.ts rename to sample/34-hmr-esm/src/common/interceptors/exception.interceptor.ts diff --git a/sample/36-hmr-esm/src/common/interceptors/timeout.interceptor.ts b/sample/34-hmr-esm/src/common/interceptors/timeout.interceptor.ts similarity index 100% rename from sample/36-hmr-esm/src/common/interceptors/timeout.interceptor.ts rename to sample/34-hmr-esm/src/common/interceptors/timeout.interceptor.ts diff --git a/sample/36-hmr-esm/src/common/middleware/logger.middleware.ts b/sample/34-hmr-esm/src/common/middleware/logger.middleware.ts similarity index 100% rename from sample/36-hmr-esm/src/common/middleware/logger.middleware.ts rename to sample/34-hmr-esm/src/common/middleware/logger.middleware.ts diff --git a/sample/36-hmr-esm/src/common/pipes/parse-int.pipe.ts b/sample/34-hmr-esm/src/common/pipes/parse-int.pipe.ts similarity index 100% rename from sample/36-hmr-esm/src/common/pipes/parse-int.pipe.ts rename to sample/34-hmr-esm/src/common/pipes/parse-int.pipe.ts diff --git a/sample/36-hmr-esm/src/common/pipes/validation.pipe.ts b/sample/34-hmr-esm/src/common/pipes/validation.pipe.ts similarity index 100% rename from sample/36-hmr-esm/src/common/pipes/validation.pipe.ts rename to sample/34-hmr-esm/src/common/pipes/validation.pipe.ts diff --git a/sample/36-hmr-esm/src/core/core.module.ts b/sample/34-hmr-esm/src/core/core.module.ts similarity index 93% rename from sample/36-hmr-esm/src/core/core.module.ts rename to sample/34-hmr-esm/src/core/core.module.ts index c6e9a519ff0..19f3aa3829e 100644 --- a/sample/36-hmr-esm/src/core/core.module.ts +++ b/sample/34-hmr-esm/src/core/core.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; -import { LoggingInterceptor } from './interceptors/logging.interceptor'; -import { TransformInterceptor } from './interceptors/transform.interceptor'; +import { LoggingInterceptor } from './interceptors/logging.interceptor.js'; +import { TransformInterceptor } from './interceptors/transform.interceptor.js'; @Module({ providers: [ diff --git a/sample/36-hmr-esm/src/core/interceptors/logging.interceptor.ts b/sample/34-hmr-esm/src/core/interceptors/logging.interceptor.ts similarity index 100% rename from sample/36-hmr-esm/src/core/interceptors/logging.interceptor.ts rename to sample/34-hmr-esm/src/core/interceptors/logging.interceptor.ts diff --git a/sample/36-hmr-esm/src/core/interceptors/transform.interceptor.ts b/sample/34-hmr-esm/src/core/interceptors/transform.interceptor.ts similarity index 100% rename from sample/36-hmr-esm/src/core/interceptors/transform.interceptor.ts rename to sample/34-hmr-esm/src/core/interceptors/transform.interceptor.ts diff --git a/sample/36-hmr-esm/src/main.ts b/sample/34-hmr-esm/src/main.ts similarity index 87% rename from sample/36-hmr-esm/src/main.ts rename to sample/34-hmr-esm/src/main.ts index 33432c98d48..180c438f26f 100644 --- a/sample/36-hmr-esm/src/main.ts +++ b/sample/34-hmr-esm/src/main.ts @@ -1,6 +1,6 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; declare const module: any; @@ -16,4 +16,4 @@ async function bootstrap() { module.hot.dispose(() => app.close()); } } -bootstrap(); +await bootstrap(); diff --git a/sample/36-hmr-esm/tsconfig.build.json b/sample/34-hmr-esm/tsconfig.build.json similarity index 100% rename from sample/36-hmr-esm/tsconfig.build.json rename to sample/34-hmr-esm/tsconfig.build.json diff --git a/sample/34-hmr-esm/tsconfig.json b/sample/34-hmr-esm/tsconfig.json new file mode 100644 index 00000000000..7826296e686 --- /dev/null +++ b/sample/34-hmr-esm/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "nodenext", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "incremental": true, + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals", + "node" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false + }, + "include": [ + "src/**/*" + ] +} diff --git a/sample/34-hmr-esm/vitest.config.e2e.mts b/sample/34-hmr-esm/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/34-hmr-esm/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/34-hmr-esm/vitest.config.mts b/sample/34-hmr-esm/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/34-hmr-esm/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/36-hmr-esm/webpack-hmr.config.cjs b/sample/34-hmr-esm/webpack-hmr.config.cjs similarity index 100% rename from sample/36-hmr-esm/webpack-hmr.config.cjs rename to sample/34-hmr-esm/webpack-hmr.config.cjs diff --git a/sample/34-using-esm-packages/.gitignore b/sample/34-using-esm-packages/.gitignore deleted file mode 100644 index 4b56acfbebf..00000000000 --- a/sample/34-using-esm-packages/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -# compiled output -/dist -/node_modules -/build - -# Logs -logs -*.log -npm-debug.log* -pnpm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# OS -.DS_Store - -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# temp directory -.temp -.tmp - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/sample/34-using-esm-packages/.prettierrc b/sample/34-using-esm-packages/.prettierrc deleted file mode 100644 index dcb72794f53..00000000000 --- a/sample/34-using-esm-packages/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/sample/34-using-esm-packages/README.md b/sample/34-using-esm-packages/README.md deleted file mode 100644 index f7bab1901aa..00000000000 --- a/sample/34-using-esm-packages/README.md +++ /dev/null @@ -1,6 +0,0 @@ -## About using ESM with Jest - -We are using the `--experimental-vm-modules` NodeJS v18 flag as explained at https://jestjs.io/docs/ecmascript-modules - -You can see how to mock an ESM package at [`app.controller.spec.ts`](./src/app.controller.spec.ts) -You can see how that the real import of an ESM package is working at [`app.e2e-spec.ts`](./test/app.e2e-spec.ts) \ No newline at end of file diff --git a/sample/34-using-esm-packages/eslint.config.mjs b/sample/34-using-esm-packages/eslint.config.mjs deleted file mode 100644 index 041d87639cf..00000000000 --- a/sample/34-using-esm-packages/eslint.config.mjs +++ /dev/null @@ -1,44 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - '@typescript-eslint/no-implied-eval': 'warn' - }, - }, -); \ No newline at end of file diff --git a/sample/34-using-esm-packages/nest-cli.json b/sample/34-using-esm-packages/nest-cli.json deleted file mode 100644 index f9aa683b1ad..00000000000 --- a/sample/34-using-esm-packages/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/sample/34-using-esm-packages/package.json b/sample/34-using-esm-packages/package.json deleted file mode 100644 index 0fb1b7e9680..00000000000 --- a/sample/34-using-esm-packages/package.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "34-using-esm-packages", - "version": "0.0.1", - "description": "", - "author": "", - "private": true, - "license": "UNLICENSED", - "type": "commonjs", - "engines": { - "node": ">=18.8" - }, - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest", - "test:watch": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --watch", - "test:cov": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --coverage", - "test:debug": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --config ./test/jest-e2e.json" - }, - "dependencies": { - "@nestjs/common": "11.x", - "@nestjs/core": "11.x", - "@nestjs/platform-express": "11.x", - "delay": "7.x", - "reflect-metadata": "0.2.2", - "rxjs": "7.x", - "superjson": "2.x" - }, - "devDependencies": { - "@nestjs/cli": "11.x", - "@nestjs/schematics": "11.x", - "@nestjs/testing": "11.x", - "@types/express": "5.x", - "@types/jest": "30.x", - "@types/node": "24.x", - "@types/supertest": "6.x", - "@typescript-eslint/eslint-plugin": "8.x", - "@typescript-eslint/parser": "8.x", - "eslint": "9.x", - "eslint-config-prettier": "10.x", - "eslint-plugin-prettier": "5.x", - "jest": "30.2.0", - "prettier": "3.x", - "source-map-support": "0.5.21", - "supertest": "7.x", - "ts-jest": "29.x", - "ts-loader": "9.x", - "ts-node": "10.x", - "tsconfig-paths": "4.x", - "typescript": "5.9.x" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } -} diff --git a/sample/34-using-esm-packages/src/app.controller.spec.ts b/sample/34-using-esm-packages/src/app.controller.spec.ts deleted file mode 100644 index a16820f1e50..00000000000 --- a/sample/34-using-esm-packages/src/app.controller.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -// NOTE: This tests nothing, it's just to show how to mock an ESM package -import { jest } from '@jest/globals'; - -// We will test the mocking feature from Jest to mock the `superjson` package for testing purposes -// We must call this before loading the module! -jest.unstable_mockModule('superjson', () => ({ - stringify: () => 'noop', -})); - -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { superJSONProvider } from './superjson.provider'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [superJSONProvider, AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe('root', () => { - it('should return the stub object', () => { - expect(appController.getHello()).toEqual({ - jsonString: 'noop', - }); - }); - }); -}); diff --git a/sample/34-using-esm-packages/src/app.controller.ts b/sample/34-using-esm-packages/src/app.controller.ts deleted file mode 100644 index 62a2f2c8399..00000000000 --- a/sample/34-using-esm-packages/src/app.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello() { - return this.appService.getHello(); - } -} diff --git a/sample/34-using-esm-packages/src/app.module.ts b/sample/34-using-esm-packages/src/app.module.ts deleted file mode 100644 index 534a90ae7a4..00000000000 --- a/sample/34-using-esm-packages/src/app.module.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Module, OnModuleInit } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { superJSONProvider } from './superjson.provider'; -import { importEsmPackage } from './import-esm-package'; - -@Module({ - imports: [], - controllers: [AppController], - providers: [ - superJSONProvider, // One way to load the ESM package is turning it into a custom provider - - AppService, - ], -}) -export class AppModule implements OnModuleInit { - // This is just to test the 'delay' ESM-only package - async onModuleInit() { - // Another way to load the ESM package is using our 'import' function directly when we need to use it - const delay = - await importEsmPackage('delay'); - - console.time('delay'); - await delay(1_000); - console.timeEnd('delay'); - } -} diff --git a/sample/34-using-esm-packages/src/app.service.ts b/sample/34-using-esm-packages/src/app.service.ts deleted file mode 100644 index 25314aec589..00000000000 --- a/sample/34-using-esm-packages/src/app.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { superJSONProvider, SuperJSON } from './superjson.provider'; - -@Injectable() -export class AppService { - constructor( - @Inject(superJSONProvider.provide) - private readonly superjson: SuperJSON, - ) {} - - getHello() { - const jsonString = this.superjson.stringify({ big: 10n }); - - return { - jsonString, - }; - } -} diff --git a/sample/34-using-esm-packages/src/import-esm-package.ts b/sample/34-using-esm-packages/src/import-esm-package.ts deleted file mode 100644 index b69aad19944..00000000000 --- a/sample/34-using-esm-packages/src/import-esm-package.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * This is the same as `import()` expression that is supposed to load ESM packages while - * preventing TypeScript from transpiling the import statement into `require()`. - */ -export const importEsmPackage = async ( - packageName: string, -): Promise => - new Function(`return import('${packageName}')`)().then( - (loadedModule: unknown) => loadedModule['default'] ?? loadedModule, - ); diff --git a/sample/34-using-esm-packages/src/main.ts b/sample/34-using-esm-packages/src/main.ts deleted file mode 100644 index 13cad38cff9..00000000000 --- a/sample/34-using-esm-packages/src/main.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule); - await app.listen(3000); -} -bootstrap(); diff --git a/sample/34-using-esm-packages/src/superjson.provider.ts b/sample/34-using-esm-packages/src/superjson.provider.ts deleted file mode 100644 index 46b53eac247..00000000000 --- a/sample/34-using-esm-packages/src/superjson.provider.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { FactoryProvider } from '@nestjs/common'; -import { importEsmPackage } from './import-esm-package'; - -// We must expose only the type definition! -export type { SuperJSON } from 'superjson'; - -export const superJSONProvider: FactoryProvider = { - provide: 'SuperJSON', - useFactory: () => importEsmPackage('superjson'), -}; diff --git a/sample/34-using-esm-packages/test/app.e2e-spec.ts b/sample/34-using-esm-packages/test/app.e2e-spec.ts deleted file mode 100644 index 7410918ed99..00000000000 --- a/sample/34-using-esm-packages/test/app.e2e-spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect((res) => { - const result = JSON.parse(res.body.jsonString); - expect(result.json.big).toBe('10'); - expect(result.meta.values.big).toEqual(['bigint']); - }); - }); -}); diff --git a/sample/34-using-esm-packages/test/jest-e2e.json b/sample/34-using-esm-packages/test/jest-e2e.json deleted file mode 100644 index e9d912f3e3c..00000000000 --- a/sample/34-using-esm-packages/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/sample/34-using-esm-packages/tsconfig.build.json b/sample/34-using-esm-packages/tsconfig.build.json deleted file mode 100644 index 64f86c6bd2b..00000000000 --- a/sample/34-using-esm-packages/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/sample/34-using-esm-packages/tsconfig.json b/sample/34-using-esm-packages/tsconfig.json deleted file mode 100644 index 95f5641cf7f..00000000000 --- a/sample/34-using-esm-packages/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false - } -} diff --git a/sample/35-use-esm-package-after-node22/.gitignore b/sample/35-use-esm-package-after-node22/.gitignore deleted file mode 100644 index 4b56acfbebf..00000000000 --- a/sample/35-use-esm-package-after-node22/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -# compiled output -/dist -/node_modules -/build - -# Logs -logs -*.log -npm-debug.log* -pnpm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# OS -.DS_Store - -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# temp directory -.temp -.tmp - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/sample/35-use-esm-package-after-node22/.prettierrc b/sample/35-use-esm-package-after-node22/.prettierrc deleted file mode 100644 index dcb72794f53..00000000000 --- a/sample/35-use-esm-package-after-node22/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/sample/35-use-esm-package-after-node22/README.md b/sample/35-use-esm-package-after-node22/README.md deleted file mode 100644 index e1f3b0cc045..00000000000 --- a/sample/35-use-esm-package-after-node22/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## How this works - -We are relying on the [`--experimental-require-module`](https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require) NodeJS v22 flag so that we can load ESM packages using `require()` - -Check out the `package.json` file. - -## About automated tests with Jest - -While Jest [does not supports](https://github.com/jestjs/jest/issues/15275) the `--experimental-require-module` flag, we cannot use Jest in this project! \ No newline at end of file diff --git a/sample/35-use-esm-package-after-node22/eslint.config.mjs b/sample/35-use-esm-package-after-node22/eslint.config.mjs deleted file mode 100644 index e297312aefb..00000000000 --- a/sample/35-use-esm-package-after-node22/eslint.config.mjs +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/unbound-method': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/35-use-esm-package-after-node22/nest-cli.json b/sample/35-use-esm-package-after-node22/nest-cli.json deleted file mode 100644 index f9aa683b1ad..00000000000 --- a/sample/35-use-esm-package-after-node22/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/sample/35-use-esm-package-after-node22/package.json b/sample/35-use-esm-package-after-node22/package.json deleted file mode 100644 index 93d31475408..00000000000 --- a/sample/35-use-esm-package-after-node22/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "35-using-esm-package-after-node22", - "version": "0.0.1", - "description": "", - "author": "", - "private": true, - "license": "UNLICENSED", - "type": "commonjs", - "engines": { - "node": ">=22" - }, - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start --exec \"node --experimental-require-module\"", - "start:dev": "nest start --exec \"node --experimental-require-module\" --watch", - "start:debug": "nest start --exec \"node --experimental-require-module\" --debug --watch", - "start:prod": "node --experimental-require-module dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix" - }, - "dependencies": { - "@nestjs/common": "11.x", - "@nestjs/core": "11.x", - "@nestjs/platform-express": "11.x", - "reflect-metadata": "0.2.2", - "rxjs": "7.x", - "superjson": "2.x" - }, - "devDependencies": { - "@nestjs/cli": "11.x", - "@nestjs/schematics": "11.x", - "@nestjs/testing": "11.x", - "@types/express": "5.x", - "@types/node": "24.x", - "@types/supertest": "6.x", - "@typescript-eslint/eslint-plugin": "8.x", - "@typescript-eslint/parser": "8.x", - "eslint": "9.x", - "eslint-config-prettier": "10.x", - "eslint-plugin-prettier": "5.x", - "prettier": "3.x", - "source-map-support": "0.5.21", - "supertest": "7.x", - "ts-loader": "9.x", - "ts-node": "10.x", - "tsconfig-paths": "4.x", - "typescript": "5.9.x" - } -} diff --git a/sample/35-use-esm-package-after-node22/src/app.controller.ts b/sample/35-use-esm-package-after-node22/src/app.controller.ts deleted file mode 100644 index 87e5a6fcad9..00000000000 --- a/sample/35-use-esm-package-after-node22/src/app.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello() { - return this.appService.getJsonStringExample(); - } -} diff --git a/sample/35-use-esm-package-after-node22/src/app.module.ts b/sample/35-use-esm-package-after-node22/src/app.module.ts deleted file mode 100644 index 86628031ca2..00000000000 --- a/sample/35-use-esm-package-after-node22/src/app.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -@Module({ - imports: [], - controllers: [AppController], - providers: [AppService], -}) -export class AppModule {} diff --git a/sample/35-use-esm-package-after-node22/src/app.service.ts b/sample/35-use-esm-package-after-node22/src/app.service.ts deleted file mode 100644 index 09b14b203f4..00000000000 --- a/sample/35-use-esm-package-after-node22/src/app.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import superjson from 'superjson'; - -@Injectable() -export class AppService { - getJsonStringExample() { - const jsonString = superjson.stringify({ big: 10n }); - - return { - jsonString, - }; - } -} diff --git a/sample/35-use-esm-package-after-node22/tsconfig.build.json b/sample/35-use-esm-package-after-node22/tsconfig.build.json deleted file mode 100644 index 64f86c6bd2b..00000000000 --- a/sample/35-use-esm-package-after-node22/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/sample/35-use-esm-package-after-node22/tsconfig.json b/sample/35-use-esm-package-after-node22/tsconfig.json deleted file mode 100644 index 95f5641cf7f..00000000000 --- a/sample/35-use-esm-package-after-node22/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false - } -} diff --git a/sample/35-zod-validation/.gitignore b/sample/35-zod-validation/.gitignore new file mode 100644 index 00000000000..408f535c087 --- /dev/null +++ b/sample/35-zod-validation/.gitignore @@ -0,0 +1,18 @@ +# dependencies +/node_modules + +# IDE +/.idea +/.awcache +/.vscode + +# misc +npm-debug.log + +# tests +/test +/coverage +/.nyc_output + +# dist +/dist diff --git a/sample/35-zod-validation/e2e/cats/cats.e2e-spec.ts b/sample/35-zod-validation/e2e/cats/cats.e2e-spec.ts new file mode 100644 index 00000000000..5e6fac93677 --- /dev/null +++ b/sample/35-zod-validation/e2e/cats/cats.e2e-spec.ts @@ -0,0 +1,47 @@ +import { INestApplication, StandardSchemaValidationPipe } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import request from 'supertest'; +import { CatsModule } from '../../src/cats/cats.module.js'; + +describe('Cats', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [CatsModule], + }).compile(); + + app = moduleRef.createNestApplication(); + app.useGlobalPipes(new StandardSchemaValidationPipe()); + await app.init(); + }); + + it(`/GET cats`, () => { + return request(app.getHttpServer()).get('/cats').expect(200).expect([]); + }); + + it(`/POST cats`, () => { + return request(app.getHttpServer()) + .post('/cats') + .send({ name: 'Pixel', age: 2, breed: 'Bombay' }) + .expect(201); + }); + + it(`/POST cats - should fail validation with invalid body`, () => { + return request(app.getHttpServer()) + .post('/cats') + .send({ name: 'Pixel', age: 'not-a-number', breed: 'Bombay' }) + .expect(400); + }); + + it(`/POST cats - should fail validation with missing fields`, () => { + return request(app.getHttpServer()) + .post('/cats') + .send({ name: 'Pixel' }) + .expect(400); + }); + + afterAll(async () => { + await app.close(); + }); +}); diff --git a/sample/35-zod-validation/package.json b/sample/35-zod-validation/package.json new file mode 100644 index 00000000000..36184bc671d --- /dev/null +++ b/sample/35-zod-validation/package.json @@ -0,0 +1,47 @@ +{ + "name": "nest-typescript-starter", + "version": "1.0.0", + "description": "Nest TypeScript starter repository", + "license": "MIT", + "scripts": { + "prebuild": "rimraf dist", + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" + }, + "dependencies": { + "@nestjs/common": "11.1.13", + "@nestjs/core": "11.1.13", + "@nestjs/platform-express": "11.1.13", + "reflect-metadata": "0.2.2", + "rimraf": "6.1.2", + "rxjs": "7.8.2", + "zod": "3.25.67" + }, + "devDependencies": { + "@nestjs/cli": "11.0.16", + "@nestjs/schematics": "11.0.9", + "@nestjs/testing": "11.1.13", + "@types/express": "5.0.6", + "@types/node": "24.10.11", + "@types/supertest": "6.0.3", + "oxlint": "1.58.0", + "prettier": "3.8.1", + "supertest": "7.2.2", + "ts-loader": "9.5.4", + "ts-node": "10.9.2", + "tsconfig-paths": "4.2.0", + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" +} diff --git a/sample/35-zod-validation/src/app.module.ts b/sample/35-zod-validation/src/app.module.ts new file mode 100644 index 00000000000..ce4c4578fa5 --- /dev/null +++ b/sample/35-zod-validation/src/app.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { CatsModule } from './cats/cats.module.js'; + +@Module({ + imports: [CatsModule], +}) +export class AppModule {} diff --git a/sample/35-zod-validation/src/cats/cats.controller.spec.ts b/sample/35-zod-validation/src/cats/cats.controller.spec.ts new file mode 100644 index 00000000000..a0231375dc9 --- /dev/null +++ b/sample/35-zod-validation/src/cats/cats.controller.spec.ts @@ -0,0 +1,53 @@ +import { Test } from '@nestjs/testing'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; + +describe('CatsController', () => { + let catsController: CatsController; + let catsService: CatsService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + controllers: [CatsController], + providers: [CatsService], + }).compile(); + + catsService = moduleRef.get(CatsService); + catsController = moduleRef.get(CatsController); + }); + + describe('findAll', () => { + it('should return an array of cats', () => { + const cats: Cat[] = [ + { + age: 2, + breed: 'Bombay', + name: 'Pixel', + }, + ]; + // @ts-ignore + catsService.cats = cats; + + expect(catsController.findAll()).toBe(cats); + }); + }); + + describe('create', () => { + it('should add a new cat', () => { + const cat: Cat = { + age: 2, + breed: 'Bombay', + name: 'Pixel', + }; + + // @ts-ignore + expect(catsService.cats).toStrictEqual([]); + + catsController.create(cat); + + // @ts-ignore + expect(catsService.cats).toStrictEqual([cat]); + }); + }); +}); diff --git a/sample/35-zod-validation/src/cats/cats.controller.ts b/sample/35-zod-validation/src/cats/cats.controller.ts new file mode 100644 index 00000000000..2df84c87cc6 --- /dev/null +++ b/sample/35-zod-validation/src/cats/cats.controller.ts @@ -0,0 +1,19 @@ +import { Body, Controller, Get, Post } from '@nestjs/common'; +import { CatsService } from './cats.service.js'; +import { CreateCatDto, CreateCatSchema } from './dto/create-cat.dto.js'; +import { Cat } from './interfaces/cat.interface.js'; + +@Controller('cats') +export class CatsController { + constructor(private readonly catsService: CatsService) {} + + @Post() + create(@Body({ schema: CreateCatSchema }) createCatDto: CreateCatDto) { + this.catsService.create(createCatDto); + } + + @Get() + findAll(): Cat[] { + return this.catsService.findAll(); + } +} diff --git a/sample/35-zod-validation/src/cats/cats.module.ts b/sample/35-zod-validation/src/cats/cats.module.ts new file mode 100644 index 00000000000..14b21cd9f14 --- /dev/null +++ b/sample/35-zod-validation/src/cats/cats.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { CatsController } from './cats.controller.js'; +import { CatsService } from './cats.service.js'; + +@Module({ + controllers: [CatsController], + providers: [CatsService], +}) +export class CatsModule {} diff --git a/sample/35-zod-validation/src/cats/cats.service.spec.ts b/sample/35-zod-validation/src/cats/cats.service.spec.ts new file mode 100644 index 00000000000..7836f4004d6 --- /dev/null +++ b/sample/35-zod-validation/src/cats/cats.service.spec.ts @@ -0,0 +1,49 @@ +import { Test } from '@nestjs/testing'; +import { CatsService } from './cats.service.js'; +import { Cat } from './interfaces/cat.interface.js'; + +describe('CatsService', () => { + let catsService: CatsService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [CatsService], + }).compile(); + + catsService = moduleRef.get(CatsService); + }); + + describe('findAll', () => { + it('should return an array of cats', () => { + const result: Cat[] = [ + { + name: 'Frajola', + age: 2, + breed: 'Stray', + }, + ]; + //@ts-ignore + catsService.cats = result; + + expect(catsService.findAll()).toBe(result); + }); + }); + + describe('create', () => { + it('should add a new cat', () => { + const cat: Cat = { + name: 'Frajola', + age: 2, + breed: 'Stray', + }; + + //@ts-ignore + expect(catsService.cats).toStrictEqual([]); + + catsService.create(cat); + + //@ts-ignore + expect(catsService.cats).toStrictEqual([cat]); + }); + }); +}); diff --git a/sample/35-zod-validation/src/cats/cats.service.ts b/sample/35-zod-validation/src/cats/cats.service.ts new file mode 100644 index 00000000000..dc5f51e15ec --- /dev/null +++ b/sample/35-zod-validation/src/cats/cats.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import { Cat } from './interfaces/cat.interface.js'; + +@Injectable() +export class CatsService { + private readonly cats: Cat[] = []; + + create(cat: Cat) { + this.cats.push(cat); + } + + findAll(): Cat[] { + return this.cats; + } +} diff --git a/sample/35-zod-validation/src/cats/dto/create-cat.dto.ts b/sample/35-zod-validation/src/cats/dto/create-cat.dto.ts new file mode 100644 index 00000000000..56e4b3567d3 --- /dev/null +++ b/sample/35-zod-validation/src/cats/dto/create-cat.dto.ts @@ -0,0 +1,9 @@ +import { z } from 'zod'; + +export const CreateCatSchema = z.object({ + name: z.string(), + age: z.number().int(), + breed: z.string(), +}); + +export type CreateCatDto = z.infer; diff --git a/sample/35-zod-validation/src/cats/interfaces/cat.interface.ts b/sample/35-zod-validation/src/cats/interfaces/cat.interface.ts new file mode 100644 index 00000000000..961c9739167 --- /dev/null +++ b/sample/35-zod-validation/src/cats/interfaces/cat.interface.ts @@ -0,0 +1,5 @@ +export interface Cat { + name: string; + age: number; + breed: string; +} diff --git a/sample/35-zod-validation/src/main.ts b/sample/35-zod-validation/src/main.ts new file mode 100644 index 00000000000..da74b6a41bb --- /dev/null +++ b/sample/35-zod-validation/src/main.ts @@ -0,0 +1,12 @@ +import { StandardSchemaValidationPipe } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module.js'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + app.useGlobalPipes(new StandardSchemaValidationPipe()); + + await app.listen(3000); + console.log(`Application is running on: ${await app.getUrl()}`); +} +await bootstrap(); diff --git a/sample/35-zod-validation/tsconfig.build.json b/sample/35-zod-validation/tsconfig.build.json new file mode 100644 index 00000000000..2fe1df273de --- /dev/null +++ b/sample/35-zod-validation/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "dist", "test", "**/*spec.ts"] +} diff --git a/sample/35-zod-validation/tsconfig.json b/sample/35-zod-validation/tsconfig.json new file mode 100644 index 00000000000..a50de140e35 --- /dev/null +++ b/sample/35-zod-validation/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "nodenext", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "incremental": true, + "strictNullChecks": true, + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false + }, + "include": [ + "src/**/*" + ] +} diff --git a/sample/35-zod-validation/vitest.config.e2e.mts b/sample/35-zod-validation/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/35-zod-validation/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/35-zod-validation/vitest.config.mts b/sample/35-zod-validation/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/35-zod-validation/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/sample/36-hmr-esm/e2e/jest-e2e.json b/sample/36-hmr-esm/e2e/jest-e2e.json deleted file mode 100644 index 72eb5c3c0a3..00000000000 --- a/sample/36-hmr-esm/e2e/jest-e2e.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ], - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", - "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], - "coverageReporters": ["json", "lcov"] -} \ No newline at end of file diff --git a/sample/36-hmr-esm/eslint.config.mjs b/sample/36-hmr-esm/eslint.config.mjs deleted file mode 100644 index ba1c7112882..00000000000 --- a/sample/36-hmr-esm/eslint.config.mjs +++ /dev/null @@ -1,42 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', - }, - }, -); \ No newline at end of file diff --git a/sample/36-hmr-esm/tsconfig.json b/sample/36-hmr-esm/tsconfig.json deleted file mode 100644 index d9c82ca9758..00000000000 --- a/sample/36-hmr-esm/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true - }, - "include": ["src/**/*"] -} diff --git a/sample/36-valibot-serializer/.gitignore b/sample/36-valibot-serializer/.gitignore new file mode 100644 index 00000000000..7f935e75080 --- /dev/null +++ b/sample/36-valibot-serializer/.gitignore @@ -0,0 +1,20 @@ +# dependencies +/node_modules + +# IDE +/.idea +/.awcache +/.vscode + +# misc +npm-debug.log + +# example +/quick-start + +# tests +/coverage +/.nyc_output + +# dist +/dist diff --git a/sample/36-valibot-serializer/e2e/serializer/serializer.e2e-spec.ts b/sample/36-valibot-serializer/e2e/serializer/serializer.e2e-spec.ts new file mode 100644 index 00000000000..f4205f6f12d --- /dev/null +++ b/sample/36-valibot-serializer/e2e/serializer/serializer.e2e-spec.ts @@ -0,0 +1,50 @@ +import { INestApplication } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import request from 'supertest'; +import { AppModule } from '../../src/app.module.js'; + +describe('Valibot Serializer (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('GET / should return serialized user without password', () => { + return request(app.getHttpServer()).get('/').expect(200).expect({ + id: 1, + firstName: 'Kamil', + lastName: 'Mysliwiec', + fullName: 'Kamil Mysliwiec', + role: 'admin', + }); + }); + + it('GET / response should not contain password', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.body).not.toHaveProperty('password'); + }); + + it('GET / should transform role to string', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(typeof response.body.role).toBe('string'); + expect(response.body.role).toBe('admin'); + }); + + it('GET / should include computed fullName', async () => { + const response = await request(app.getHttpServer()).get('/').expect(200); + + expect(response.body.fullName).toBe('Kamil Mysliwiec'); + }); +}); diff --git a/sample/36-valibot-serializer/package.json b/sample/36-valibot-serializer/package.json new file mode 100644 index 00000000000..df4078cfcd1 --- /dev/null +++ b/sample/36-valibot-serializer/package.json @@ -0,0 +1,47 @@ +{ + "name": "nest-typescript-starter", + "version": "1.0.0", + "description": "Nest TypeScript starter repository", + "license": "MIT", + "scripts": { + "prebuild": "rimraf dist", + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "oxlint src --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:cov": "vitest run --coverage", + "test:debug": "vitest --inspect-brk --no-file-parallelism", + "test:e2e": "vitest run --config ./vitest.config.e2e.mts" + }, + "dependencies": { + "@nestjs/common": "11.1.13", + "@nestjs/core": "11.1.13", + "@nestjs/platform-express": "11.1.13", + "reflect-metadata": "0.2.2", + "rimraf": "6.1.2", + "rxjs": "7.8.2", + "valibot": "1.2.0" + }, + "devDependencies": { + "@nestjs/cli": "11.0.16", + "@nestjs/schematics": "11.0.9", + "@nestjs/testing": "11.1.13", + "@types/express": "5.0.6", + "@types/node": "24.10.13", + "@types/supertest": "6.0.3", + "oxlint": "1.58.0", + "prettier": "3.8.1", + "supertest": "7.2.2", + "ts-loader": "9.5.4", + "ts-node": "10.9.2", + "tsconfig-paths": "4.2.0", + "typescript": "6.0.2", + "vitest": "4.1.2" + }, + "type": "module" +} diff --git a/sample/36-valibot-serializer/src/app.controller.ts b/sample/36-valibot-serializer/src/app.controller.ts new file mode 100644 index 00000000000..f6061af67d8 --- /dev/null +++ b/sample/36-valibot-serializer/src/app.controller.ts @@ -0,0 +1,25 @@ +import { + Controller, + Get, + SerializeOptions, + UseInterceptors, +} from '@nestjs/common'; +import { StandardSchemaSerializerInterceptor } from '@nestjs/common/serializer/standard-schema-serializer.interceptor.js'; +import { User } from './entities/user.interface.js'; +import { UserResponseSchema } from './schemas/user-response.schema.js'; + +@Controller() +@UseInterceptors(StandardSchemaSerializerInterceptor) +export class AppController { + @Get() + @SerializeOptions({ schema: UserResponseSchema }) + findOne(): User { + return { + id: 1, + firstName: 'Kamil', + lastName: 'Mysliwiec', + password: 'password', + role: { id: 1, name: 'admin' }, + }; + } +} diff --git a/sample/36-valibot-serializer/src/app.module.ts b/sample/36-valibot-serializer/src/app.module.ts new file mode 100644 index 00000000000..52bb569b5d1 --- /dev/null +++ b/sample/36-valibot-serializer/src/app.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller.js'; + +@Module({ + controllers: [AppController], +}) +export class AppModule {} diff --git a/sample/36-valibot-serializer/src/entities/role.interface.ts b/sample/36-valibot-serializer/src/entities/role.interface.ts new file mode 100644 index 00000000000..eb6e61c9e17 --- /dev/null +++ b/sample/36-valibot-serializer/src/entities/role.interface.ts @@ -0,0 +1,4 @@ +export interface Role { + id: number; + name: string; +} diff --git a/sample/36-valibot-serializer/src/entities/user.interface.ts b/sample/36-valibot-serializer/src/entities/user.interface.ts new file mode 100644 index 00000000000..b9cb7ed171f --- /dev/null +++ b/sample/36-valibot-serializer/src/entities/user.interface.ts @@ -0,0 +1,9 @@ +import { Role } from './role.interface.js'; + +export interface User { + id: number; + firstName: string; + lastName: string; + password: string; + role: Role; +} diff --git a/sample/35-use-esm-package-after-node22/src/main.ts b/sample/36-valibot-serializer/src/main.ts similarity index 53% rename from sample/35-use-esm-package-after-node22/src/main.ts rename to sample/36-valibot-serializer/src/main.ts index 13cad38cff9..0b59a74b083 100644 --- a/sample/35-use-esm-package-after-node22/src/main.ts +++ b/sample/36-valibot-serializer/src/main.ts @@ -1,8 +1,10 @@ import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; +import { AppModule } from './app.module.js'; async function bootstrap() { const app = await NestFactory.create(AppModule); + await app.listen(3000); + console.log(`Application is running on: ${await app.getUrl()}`); } -bootstrap(); +await bootstrap(); diff --git a/sample/36-valibot-serializer/src/schemas/user-response.schema.ts b/sample/36-valibot-serializer/src/schemas/user-response.schema.ts new file mode 100644 index 00000000000..d927f55a2a1 --- /dev/null +++ b/sample/36-valibot-serializer/src/schemas/user-response.schema.ts @@ -0,0 +1,29 @@ +import * as v from 'valibot'; + +/** + * Valibot schema that defines the serialized shape of a User response. + * + * - Excludes `password` (not present in the schema) + * - Includes a computed `fullName` field + * - Transforms `role` from a nested object to just the role name string + */ +export const UserResponseSchema = v.pipe( + v.object({ + id: v.number(), + firstName: v.string(), + lastName: v.string(), + role: v.object({ + id: v.number(), + name: v.string(), + }), + }), + v.transform(input => ({ + id: input.id, + firstName: input.firstName, + lastName: input.lastName, + fullName: `${input.firstName} ${input.lastName}`, + role: input.role.name, + })), +); + +export type UserResponse = v.InferOutput; diff --git a/sample/36-valibot-serializer/tsconfig.build.json b/sample/36-valibot-serializer/tsconfig.build.json new file mode 100644 index 00000000000..2fe1df273de --- /dev/null +++ b/sample/36-valibot-serializer/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "dist", "test", "**/*spec.ts"] +} diff --git a/sample/36-valibot-serializer/tsconfig.json b/sample/36-valibot-serializer/tsconfig.json new file mode 100644 index 00000000000..a50de140e35 --- /dev/null +++ b/sample/36-valibot-serializer/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "nodenext", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "incremental": true, + "strictNullChecks": true, + "skipLibCheck": true, + "moduleResolution": "nodenext", + "types": [ + "vitest/globals" + ], + "rootDir": "./src", + "useDefineForClassFields": false, + "strictPropertyInitialization": false + }, + "include": [ + "src/**/*" + ] +} diff --git a/sample/36-valibot-serializer/vitest.config.e2e.mts b/sample/36-valibot-serializer/vitest.config.e2e.mts new file mode 100644 index 00000000000..653d34e68c5 --- /dev/null +++ b/sample/36-valibot-serializer/vitest.config.e2e.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['e2e/**/*.e2e-spec.ts'], + } +}); diff --git a/sample/36-valibot-serializer/vitest.config.mts b/sample/36-valibot-serializer/vitest.config.mts new file mode 100644 index 00000000000..baa9406a7df --- /dev/null +++ b/sample/36-valibot-serializer/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['src/**/*.spec.ts'], + } +}); diff --git a/tools/benchmarks/tsconfig.json b/tools/benchmarks/tsconfig.json index df3c10ce597..846a91127ec 100644 --- a/tools/benchmarks/tsconfig.json +++ b/tools/benchmarks/tsconfig.json @@ -5,7 +5,7 @@ "compilerOptions": { "lib": ["es2023"], "module": "node16", - "target": "es2022", + "target": "es2023", "strict": true, "esModuleInterop": true, diff --git a/tools/gulp/config.ts b/tools/gulp/config.ts index 85ffc5aaed8..cd0dd5b4433 100644 --- a/tools/gulp/config.ts +++ b/tools/gulp/config.ts @@ -1,4 +1,4 @@ -import { getDirs } from './util/task-helpers'; +import { getDirs } from './util/task-helpers.js'; // All paths are related to the base dir export const source = 'packages'; diff --git a/tools/gulp/gulpfile.ts b/tools/gulp/gulpfile.ts index 1e62007616f..dbe0f419ccb 100644 --- a/tools/gulp/gulpfile.ts +++ b/tools/gulp/gulpfile.ts @@ -1,4 +1,4 @@ -import './tasks/clean'; -import './tasks/copy-misc'; -import './tasks/move'; -import './tasks/samples'; +import './tasks/clean.js'; +import './tasks/copy-misc.js'; +import './tasks/move.js'; +import './tasks/samples.js'; diff --git a/tools/gulp/tasks/clean.ts b/tools/gulp/tasks/clean.ts index 19b8322ebca..1db41db83ff 100644 --- a/tools/gulp/tasks/clean.ts +++ b/tools/gulp/tasks/clean.ts @@ -1,7 +1,7 @@ -import { task, src, series } from 'gulp'; -import { source } from '../config'; -import * as clean from 'gulp-clean'; -import * as deleteEmpty from 'delete-empty'; +import deleteEmpty from 'delete-empty'; +import { series, src, task } from 'gulp'; +import clean from 'gulp-clean'; +import { source } from '../config.js'; /** * Cleans the build output assets from the packages folders diff --git a/tools/gulp/tasks/copy-misc.ts b/tools/gulp/tasks/copy-misc.ts index 8c3091f1164..85deba9bf64 100644 --- a/tools/gulp/tasks/copy-misc.ts +++ b/tools/gulp/tasks/copy-misc.ts @@ -1,5 +1,5 @@ -import { task, src, dest } from 'gulp'; -import { packagePaths } from '../config'; +import { dest, src, task } from 'gulp'; +import { packagePaths } from '../config.js'; /** * Copies assets like Readme.md or LICENSE from the project base path diff --git a/tools/gulp/tasks/move.ts b/tools/gulp/tasks/move.ts index 666901c39a1..677cccbad13 100644 --- a/tools/gulp/tasks/move.ts +++ b/tools/gulp/tasks/move.ts @@ -1,9 +1,13 @@ import { dest, src, task } from 'gulp'; -import { join } from 'path'; -import { samplePath } from '../config'; -import { containsPackageJson, getDirs } from '../util/task-helpers'; +import { join } from 'node:path'; +import { samplePath } from '../config.js'; +import { containsPackageJson, getDirs } from '../util/task-helpers.js'; -const distFiles = src(['packages/**/*.js', 'packages/**/*.d.ts']); +const distFiles = src([ + 'packages/**/*.js', + 'packages/**/*.d.ts', + 'packages/**/package.json', +]); /** * Moves the compiled nest files into "node_module" folder. diff --git a/tools/gulp/tasks/samples.ts b/tools/gulp/tasks/samples.ts index c1c75269fcd..e5cbac5d399 100644 --- a/tools/gulp/tasks/samples.ts +++ b/tools/gulp/tasks/samples.ts @@ -1,12 +1,16 @@ import { blue, magenta } from 'ansis'; -import * as childProcess from 'child_process'; -import { execFile as execFileCb } from 'child_process'; -import * as log from 'fancy-log'; +import log from 'fancy-log'; import { task } from 'gulp'; -import { resolve } from 'path'; -import { promisify } from 'util'; -import { samplePath } from '../config'; -import { containsPackageJson, getDirs } from '../util/task-helpers'; +import * as childProcess from 'node:child_process'; +import { execFile as execFileCb } from 'node:child_process'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { promisify } from 'node:util'; +import { samplePath } from '../config.js'; +import { containsPackageJson, getDirs } from '../util/task-helpers.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const exec = promisify(childProcess.exec); const execFile = promisify(execFileCb); @@ -28,6 +32,21 @@ async function executeNpmScriptInSamples( directories.splice(prismaSampleIndex, 1); } + // TODO: samples that use third-party compiler plugins (e.g. @nestjs/swagger, + // @nestjs/graphql) are excluded because move:samples copies local ESM builds + // into node_modules, but these CJS plugins load framework packages via require(). + const samplesWithCompilerPlugins = [ + '11-swagger', + '23-graphql-code-first', + '33-graphql-mercurius', + ]; + for (const sample of samplesWithCompilerPlugins) { + const idx = directories.indexOf(`${samplePath}/${sample}`); + if (idx !== -1) { + directories.splice(idx, 1); + } + } + // A dictionary that maps the sample number to the minimum Node.js version // required to execute any scripts. const minNodejsVersionBySampleNumber = { @@ -118,6 +137,10 @@ task('install:samples', async () => ), ); task('build:samples', async () => executeNpmScriptInSamples('npm run build')); +// TODO: re-enable test:samples and test:e2e:samples once third-party @nestjs/* +// packages (e.g. @nestjs/typeorm, @nestjs/swagger) ship ESM builds. Currently, +// move:samples overwrites published CJS builds with local ESM, breaking CJS +// require() chains in these packages. task('test:samples', async () => executeNpmScriptInSamples('npm run test', '--passWithNoTests'), ); diff --git a/tools/gulp/tsconfig.json b/tools/gulp/tsconfig.json index 0432178305b..4040caeb108 100644 --- a/tools/gulp/tsconfig.json +++ b/tools/gulp/tsconfig.json @@ -3,22 +3,18 @@ "experimentalDecorators": true, "noUnusedParameters": false, "noUnusedLocals": false, - "module": "commonjs", - "moduleResolution": "node", + "module": "Node16", + "moduleResolution": "Node16", "outDir": "../../dist/tools/gulp", "strictNullChecks": true, "strictFunctionTypes": true, "noImplicitThis": true, "noEmitOnError": true, "noImplicitAny": false, - "target": "ES2021", - "types": [ - "node" - ], + "target": "ES2023", + "types": ["node"], "typeRoots": ["./typings", "../../node_modules/@types/"], - "baseUrl": ".", + "baseUrl": "." }, - "files": [ - "gulpfile.ts" - ] + "files": ["gulpfile.ts"] } diff --git a/tools/gulp/util/task-helpers.ts b/tools/gulp/util/task-helpers.ts index 9e9e65e3cd5..1977b096f00 100644 --- a/tools/gulp/util/task-helpers.ts +++ b/tools/gulp/util/task-helpers.ts @@ -1,5 +1,5 @@ -import { readdirSync, statSync } from 'fs'; -import { join } from 'path'; +import { readdirSync, statSync } from 'node:fs'; +import { join } from 'node:path'; function isDirectory(path: string) { return statSync(path).isDirectory(); diff --git a/tsconfig.json b/tsconfig.json index b9e2adb889f..03749f854a9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,8 @@ { "compilerOptions": { - "module": "commonjs", + "module": "Node16", + "moduleResolution": "Node16", + "esModuleInterop": true, "noImplicitAny": false, "noUnusedLocals": false, "removeComments": true, @@ -10,12 +12,13 @@ "noLib": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, + "useDefineForClassFields": false, "useUnknownInCatchVariables": false, - "target": "ES2021", + "target": "ES2023", "sourceMap": true, "allowJs": false, "outDir": "dist", - "baseUrl": ".", + "types": ["node"], "paths": { "@nestjs/common": ["./packages/common"], "@nestjs/common/*": ["./packages/common/*"], @@ -34,7 +37,7 @@ "@nestjs/platform-fastify": ["./packages/platform-fastify"], "@nestjs/platform-fastify/*": ["./packages/platform-fastify/*"], "@nestjs/platform-socket.io": ["./packages/platform-socket.io"], - "@nestjs/platform-socket.io/*": ["./packages/platform-socket.io/*"], + "@nestjs/platform-socket.io/*": ["./packages/platform-socket.io/*"] } }, "include": ["packages/**/*", "integration/**/*"], diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 204fc267bb6..3d12e5bbce9 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -1,5 +1,13 @@ { "extends": "./tsconfig.json", - "include": ["integration/**/*", "integration/**/*.spec.ts", "packages/**/*.spec.ts"], + "compilerOptions": { + "rootDir": ".", + "types": ["vitest/globals"] + }, + "include": [ + "integration/**/*", + "integration/**/*.spec.ts", + "packages/**/*.spec.ts" + ], "exclude": ["node_modules", "dist"] -} \ No newline at end of file +} diff --git a/vitest.config.coverage.mts b/vitest.config.coverage.mts new file mode 100644 index 00000000000..4569710b787 --- /dev/null +++ b/vitest.config.coverage.mts @@ -0,0 +1,80 @@ +import { existsSync } from 'fs'; +import type { Plugin } from 'vite'; +import { defineConfig } from 'vitest/config'; + +/** + * Resolves `.js` imports to `.ts` source files when both exist side-by-side. + * + * The NestJS monorepo keeps compiled `.js` output next to the `.ts` sources. + * Without this plugin, Vite resolves `import '…/foo.js'` to the compiled JS, + * making coverage track the wrong file (and then excluded by `*.js` patterns). + */ +function resolveTypescriptSource(): Plugin { + return { + name: 'resolve-ts-source', + enforce: 'pre', + async resolveId(source, importer, options) { + if (!importer) return null; + const resolved = await this.resolve(source, importer, { + ...options, + skipSelf: true, + }); + if (resolved && !resolved.external && resolved.id.endsWith('.js')) { + const tsPath = resolved.id.replace(/\.js$/, '.ts'); + if (existsSync(tsPath)) return tsPath; + } + return null; + }, + }; +} + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['packages/**/*.spec.ts'], + alias: { + '@nestjs/common': './packages/common', + '@nestjs/core': './packages/core', + '@nestjs/microservices': './packages/microservices', + '@nestjs/websockets': './packages/websockets', + '@nestjs/testing': './packages/testing', + '@nestjs/platform-express': './packages/platform-express', + '@nestjs/platform-ws': './packages/platform-ws', + '@nestjs/platform-fastify': './packages/platform-fastify', + '@nestjs/platform-socket.io': './packages/platform-socket.io', + }, + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'clover', 'lcov'], + reportOnFailure: true, + include: ['packages/**/*.ts'], + exclude: [ + '**/node_modules/**', + '**/*.d.ts', + '**/*.spec.ts', + 'packages/**/test/**', + 'packages/**/adapters/*.ts', + 'packages/**/nest-*.ts', + 'packages/core/errors/**/*', + 'packages/common/exceptions/*.ts', + 'packages/common/utils/load-package.util.ts', + 'packages/microservices/exceptions/', + 'packages/microservices/microservices-module.ts', + 'packages/core/middleware/middleware-module.ts', + 'packages/core/discovery/discovery-service.ts', + 'packages/core/injector/module-ref.ts', + 'packages/core/injector/instance-links-host.ts', + 'packages/core/helpers/context-id-factory.ts', + 'packages/websockets/socket-module.ts', + 'packages/common/cache/**/*', + 'packages/common/serializer/**/*', + 'packages/common/services/*.ts', + ], + }, + setupFiles: ['reflect-metadata'], + // Allow tests to fail — we only want the coverage data + passWithNoTests: true, + }, + plugins: [resolveTypescriptSource()], +}); diff --git a/vitest.config.integration.mts b/vitest.config.integration.mts new file mode 100644 index 00000000000..6a015c1ffc3 --- /dev/null +++ b/vitest.config.integration.mts @@ -0,0 +1,57 @@ +import { existsSync } from 'fs'; +import type { Plugin } from 'vite'; +import { defineConfig } from 'vitest/config'; + +/** + * Resolves `.js` imports to `.ts` source files when both exist side-by-side. + * + * The NestJS monorepo keeps compiled `.js` output next to the `.ts` sources. + * Without this plugin, Vite resolves `import '…/foo.js'` to the compiled JS + * instead of the original TypeScript source. + */ +function resolveTypescriptSource(): Plugin { + return { + name: 'resolve-ts-source', + enforce: 'pre', + async resolveId(source, importer, options) { + if (!importer) return null; + const resolved = await this.resolve(source, importer, { + ...options, + skipSelf: true, + }); + if (resolved && !resolved.external && resolved.id.endsWith('.js')) { + const tsPath = resolved.id.replace(/\.js$/, '.ts'); + if (existsSync(tsPath)) return tsPath; + } + return null; + }, + }; +} + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['integration/**/*.spec.ts'], + exclude: [ + '**/node_modules/**', + // Excluded until third-party @nestjs/* packages support ESM. + // CJS require() of workspace packages causes a dual-package hazard + // (different class instances for ModuleRef, etc.). + 'integration/mongoose/**', + 'integration/typeorm/**', + 'integration/graphql-code-first/**', + 'integration/graphql-schema-first/**', + // TODO: remove once these are ESM-compatible + // Uses @nestjs/mapped-types (CJS) — same dual-package hazard + 'integration/inspector/**', + 'integration/repl/e2e/repl.spec.ts', + ], + testTimeout: 30_000, + hookTimeout: 30_000, + setupFiles: ['reflect-metadata'], + reporters: ['default'], + fileParallelism: false, + }, + plugins: [resolveTypescriptSource()], +}); diff --git a/vitest.config.mts b/vitest.config.mts new file mode 100644 index 00000000000..06d29296c53 --- /dev/null +++ b/vitest.config.mts @@ -0,0 +1,50 @@ +import { existsSync } from 'fs'; +import type { Plugin } from 'vite'; +import { defineConfig } from 'vitest/config'; + +/** + * Resolves `.js` imports to `.ts` source files when both exist side-by-side. + * + * The NestJS monorepo keeps compiled `.js` output next to the `.ts` sources. + * Without this plugin, Vite resolves `import '…/foo.js'` to the compiled JS + * instead of the original TypeScript source. + */ +function resolveTypescriptSource(): Plugin { + return { + name: 'resolve-ts-source', + enforce: 'pre', + async resolveId(source, importer, options) { + if (!importer) return null; + const resolved = await this.resolve(source, importer, { + ...options, + skipSelf: true, + }); + if (resolved && !resolved.external && resolved.id.endsWith('.js')) { + const tsPath = resolved.id.replace(/\.js$/, '.ts'); + if (existsSync(tsPath)) return tsPath; + } + return null; + }, + }; +} + +export default defineConfig({ + test: { + globals: true, + root: './', + include: ['packages/**/*.spec.ts'], + alias: { + '@nestjs/common': './packages/common', + '@nestjs/core': './packages/core', + '@nestjs/microservices': './packages/microservices', + '@nestjs/websockets': './packages/websockets', + '@nestjs/testing': './packages/testing', + '@nestjs/platform-express': './packages/platform-express', + '@nestjs/platform-ws': './packages/platform-ws', + '@nestjs/platform-fastify': './packages/platform-fastify', + '@nestjs/platform-socket.io': './packages/platform-socket.io', + }, + setupFiles: ['reflect-metadata'], + }, + plugins: [resolveTypescriptSource()], +});