diff --git a/apps/cli/package.json b/apps/cli/package.json index e80f3113..9c534ebc 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -11,7 +11,6 @@ "@types/lodash": "catalog:", "@types/supertest": "^6.0.3", "@types/pluralize": "^0.0.33", - "@types/qs": "^6.9.15", "@types/signale": "^1.4.7", "@types/semver": "catalog:", "supertest": "^7.1.4", @@ -33,7 +32,6 @@ "commander": "^13.1.0", "chalk": "^4.1.2", "parse-duration": "^2.1.5", - "qs": "^6.14.1", "dotenv": "^16.4.5", "source-map-support": "^0.5.21", "agentkeepalive": "^4.6.0", diff --git a/apps/cli/src/command/helper.ts b/apps/cli/src/command/helper.ts index 71926a4e..6cfff3ef 100644 --- a/apps/cli/src/command/helper.ts +++ b/apps/cli/src/command/helper.ts @@ -5,11 +5,55 @@ import { has } from 'lodash'; import { existsSync } from 'node:fs'; import { resolve } from 'node:path'; import parseDuration from 'parse-duration'; -import qs from 'qs'; export interface BaseOptions { verbose: number; } + +export const parseLabelSelector = ( + val: string, + previous: Record = {}, +) => { + const current = val.trim(); + if (!current) { + throw new InvalidArgumentError( + 'Label selector must be in the format key=value', + ); + } + + const parsed = current.split(',').reduce>( + (acc, pair) => { + const item = pair.trim(); + const separatorIndex = item.indexOf('='); + + if ( + !item || + separatorIndex <= 0 || + separatorIndex === item.length - 1 + ) { + throw new InvalidArgumentError( + `Invalid label selector "${item}". Expected key=value`, + ); + } + + const key = item.slice(0, separatorIndex).trim(); + const value = item.slice(separatorIndex + 1).trim(); + + if (!key || !value) { + throw new InvalidArgumentError( + `Invalid label selector "${item}". Expected key=value`, + ); + } + + acc[key] = value; + return acc; + }, + {}, + ); + + return Object.assign(previous, parsed); +}; + export class BaseCommand< OPTS extends BaseOptions = BaseOptions, > extends Command { @@ -129,9 +173,7 @@ export class BackendCommand< new Option( '--label-selector ', 'filter resources by labels', - ).argParser((val, previous: Record = {}) => - Object.assign(previous, qs.parse(val, { delimiter: ',' })), - ), + ).argParser(parseLabelSelector), ) .addOption( new Option( diff --git a/apps/cli/src/command/utils.spec.ts b/apps/cli/src/command/utils.spec.ts index 12733cce..cd1a3bdd 100644 --- a/apps/cli/src/command/utils.spec.ts +++ b/apps/cli/src/command/utils.spec.ts @@ -1,7 +1,10 @@ import * as ADCSDK from '@api7/adc-sdk'; +import { InvalidArgumentError } from 'commander'; +import { parseLabelSelector } from './helper'; import { fillLabels, + filterConfiguration, recursiveRemoveMetadataField, recursiveReplaceEnvVars, } from './utils'; @@ -200,6 +203,74 @@ describe('CLI utils', () => { }); }); + it('should filter configuration by exact label key match', () => { + const [filtered, removed] = filterConfiguration( + { + services: [ + { + name: 'match-by-name', + labels: { name: 'yanglao_wx_pgm' }, + }, + { + name: 'should-not-match-appname', + labels: { appname: 'yanglao_wx_pgm' }, + }, + { + name: 'should-not-match-different-value', + labels: { name: 'other' }, + }, + ], + } as ADCSDK.Configuration, + { name: 'yanglao_wx_pgm' }, + ); + + expect(filtered.services).toEqual([ + { + name: 'match-by-name', + labels: { name: 'yanglao_wx_pgm' }, + }, + ]); + expect(removed.services).toEqual([ + { + name: 'should-not-match-appname', + labels: { appname: 'yanglao_wx_pgm' }, + }, + { + name: 'should-not-match-different-value', + labels: { name: 'other' }, + }, + ]); + }); + + it('should parse label selectors in key=value format only', () => { + expect(parseLabelSelector('name=yanglao_wx_pgm')).toEqual({ + name: 'yanglao_wx_pgm', + }); + expect(parseLabelSelector('release=2026-03-13T12:00:00Z')).toEqual({ + release: '2026-03-13T12:00:00Z', + }); + expect(parseLabelSelector('url=http://a')).toEqual({ + url: 'http://a', + }); + expect( + parseLabelSelector('name=yanglao_wx_pgm,team=adc', { env: 'prod' }), + ).toEqual({ + env: 'prod', + name: 'yanglao_wx_pgm', + team: 'adc', + }); + }); + + it('should reject invalid label selector formats', () => { + expect(() => parseLabelSelector('name:yanglao_wx_pgm')).toThrow( + InvalidArgumentError, + ); + expect(() => parseLabelSelector('name=')).toThrow(InvalidArgumentError); + expect(() => parseLabelSelector('=yanglao_wx_pgm')).toThrow( + InvalidArgumentError, + ); + }); + it('should remove metadata from dump result', () => { const config = { services: [ diff --git a/apps/cli/src/server/sync.ts b/apps/cli/src/server/sync.ts index 2c538b99..629f432a 100644 --- a/apps/cli/src/server/sync.ts +++ b/apps/cli/src/server/sync.ts @@ -7,7 +7,6 @@ import { omit, toString } from 'lodash'; import { lastValueFrom, toArray } from 'rxjs'; import { - fillLabels, filterConfiguration, filterResourceType, loadBackend, @@ -52,11 +51,15 @@ export const syncHandler: RequestHandler< // load local configuration and validate it //TODO: merged with the listr task - const local = filterResourceType( + let local = filterResourceType( task.config, task.opts.includeResourceType, task.opts.excludeResourceType, ) as ADCSDK.InternalConfiguration; + [local] = filterConfiguration( + local, + task.opts.labelSelector, + ) as [ADCSDK.InternalConfiguration, ADCSDK.Configuration]; if (task.opts.lint) { const result = check(local); if (!result.success) @@ -65,7 +68,6 @@ export const syncHandler: RequestHandler< errors: result.error.issues, }); } - fillLabels(local, task.opts.labelSelector); // load and filter remote configuration const backend = loadBackend(task.opts.backend, { @@ -134,10 +136,10 @@ export const syncHandler: RequestHandler< task.opts.backend !== 'apisix-standalone' ? generateOutput([results, successes, faileds]) : generateOutputForAPISIXStandalone(diff, [ - results, - successes, - faileds, - ]); + results, + successes, + faileds, + ]); logger.log({ level: 'debug', @@ -173,7 +175,7 @@ const formatAxiosResponse = (axiosResponse: AxiosResponse) => ({ method: axiosResponse.config.method, headers: ((axiosResponse.config.headers['X-API-KEY'] = '*****'), - axiosResponse.config.headers), + axiosResponse.config.headers), data: axiosResponse.config.data, }, }); diff --git a/apps/cli/src/tasks/load_local.spec.ts b/apps/cli/src/tasks/load_local.spec.ts new file mode 100644 index 00000000..a9160af0 --- /dev/null +++ b/apps/cli/src/tasks/load_local.spec.ts @@ -0,0 +1,50 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { dump } from 'js-yaml'; +import { Listr } from 'listr2'; +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; + +import { LoadLocalConfigurationTask } from './load_local'; + +describe('LoadLocalConfigurationTask', () => { + it('filters local resources by exact label key match', async () => { + const dir = mkdtempSync(path.join(tmpdir(), 'adc-load-local-')); + const file = path.join(dir, 'adc.yaml'); + + writeFileSync( + file, + dump({ + services: [ + { + name: 'match-by-name', + labels: { name: 'yanglao_wx_pgm' }, + }, + { + name: 'should-not-match-appname', + labels: { appname: 'yanglao_wx_pgm' }, + }, + ], + } satisfies ADCSDK.Configuration), + 'utf8', + ); + + const ctx: { local: ADCSDK.Configuration } = { local: {} }; + + try { + await new Listr( + [LoadLocalConfigurationTask([file], { name: 'yanglao_wx_pgm' })], + { ctx }, + ).run(); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + + expect(ctx.local.services).toEqual([ + { + name: 'match-by-name', + labels: { name: 'yanglao_wx_pgm' }, + }, + ]); + }); +}); diff --git a/apps/cli/src/tasks/load_local.ts b/apps/cli/src/tasks/load_local.ts index fef15f21..514a57ce 100644 --- a/apps/cli/src/tasks/load_local.ts +++ b/apps/cli/src/tasks/load_local.ts @@ -7,7 +7,7 @@ import { ListrTask } from 'listr2'; import path from 'node:path'; import { - fillLabels, + filterConfiguration, filterResourceType, mergeKVConfigurations, recursiveReplaceEnvVars, @@ -100,8 +100,7 @@ export const LoadLocalConfigurationTask = ( title: 'Filter configuration', enabled: !!labelSelector && Object.keys(labelSelector).length > 0, task: async () => { - // Merge label selectors from CLI inputs to each resource - fillLabels(ctx.local, labelSelector); + [ctx.local] = filterConfiguration(ctx.local, labelSelector); }, }, ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index efc63eb2..8cb47217 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,7 +76,7 @@ importers: version: 22.3.3(@babel/traverse@7.28.0)(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.16.5)(@zkochan/js-yaml@0.0.7)(babel-plugin-macros@3.1.0)(eslint@9.33.0(jiti@2.4.2))(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17)))(ts-node@10.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.16.5)(typescript@5.9.3))(typescript@5.9.3) '@nx/vitest': specifier: 22.3.3 - version: 22.3.3(@babel/traverse@7.28.0)(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17)))(typescript@5.9.3)(vite@7.1.9(@types/node@22.16.5)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0))(vitest@4.0.13) + version: 22.3.3(@babel/traverse@7.28.0)(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17)))(typescript@5.9.3)(vite@7.1.9(@types/node@22.16.5)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0))(vitest@4.0.13(@types/node@22.16.5)(@vitest/ui@4.0.13)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0)) '@nx/web': specifier: 22.3.3 version: 22.3.3(@babel/traverse@7.28.0)(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))) @@ -112,7 +112,7 @@ importers: version: 8.41.0(eslint@9.33.0(jiti@2.4.2))(typescript@5.9.3) '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.13(vitest@4.0.13) + version: 4.0.13(vitest@4.0.13(@types/node@22.16.5)(@vitest/ui@4.0.13)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0)) '@vitest/ui': specifier: 'catalog:' version: 4.0.13(vitest@4.0.13) @@ -197,9 +197,6 @@ importers: pluralize: specifier: ^8.0.0 version: 8.0.0 - qs: - specifier: ^6.14.1 - version: 6.14.1 rxjs: specifier: 'catalog:' version: 7.8.2 @@ -243,9 +240,6 @@ importers: '@types/pluralize': specifier: ^0.0.33 version: 0.0.33 - '@types/qs': - specifier: ^6.9.15 - version: 6.9.15 '@types/semver': specifier: 'catalog:' version: 7.7.1 @@ -1753,25 +1747,21 @@ packages: resolution: {integrity: sha512-NmPeCexWIZHW9RM3lDdFENN9C3WtlQ5L4RSNFESIjreS921rgePhulsszYdGnHdcnKPYlBBJnX/NxVsfioBbnQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@nx/nx-linux-arm64-musl@22.3.3': resolution: {integrity: sha512-K02U88Q0dpvCfmSXXvY7KbYQSa1m+mkYeqDBRHp11yHk1GoIqaHp8oEWda7FV4gsriNExPSS5tX1/QGVoLZrCw==} cpu: [arm64] os: [linux] - libc: [musl] '@nx/nx-linux-x64-gnu@22.3.3': resolution: {integrity: sha512-04TEbvgwRaB9ifr39YwJmWh3RuXb4Ry4m84SOJyjNXAfPrepcWgfIQn1VL2ul1Ybq+P023dLO9ME8uqFh6j1YQ==} cpu: [x64] os: [linux] - libc: [glibc] '@nx/nx-linux-x64-musl@22.3.3': resolution: {integrity: sha512-uxBXx5q+S5OGatbYDxnamsKXRKlYn+Eq1nrCAHaf8rIfRoHlDiRV2PqtWuF+O2pxR5FWKpvr+/sZtt9rAf7KMw==} cpu: [x64] os: [linux] - libc: [musl] '@nx/nx-win32-arm64-msvc@22.3.3': resolution: {integrity: sha512-aOwlfD6ZA1K6hjZtbhBSp7s1yi3sHbMpLCa4stXzfhCCpKUv46HU/EdiWdE1N8AsyNFemPZFq81k1VTowcACdg==} @@ -1835,42 +1825,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.1': resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} @@ -1970,67 +1954,56 @@ packages: resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.52.5': resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.52.5': resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.52.5': resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.52.5': resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.52.5': resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.52.5': resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.52.5': resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.52.5': resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.52.5': resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.52.5': resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openharmony-arm64@4.52.5': resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} @@ -2209,28 +2182,24 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [glibc] '@swc/core-linux-arm64-musl@1.13.5': resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [musl] '@swc/core-linux-x64-gnu@1.13.5': resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [glibc] '@swc/core-linux-x64-musl@1.13.5': resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [musl] '@swc/core-win32-arm64-msvc@1.13.5': resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} @@ -2554,49 +2523,41 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] - libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -8616,7 +8577,7 @@ snapshots: '@nx/nx-win32-x64-msvc@22.3.3': optional: true - '@nx/vitest@22.3.3(@babel/traverse@7.28.0)(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17)))(typescript@5.9.3)(vite@7.1.9(@types/node@22.16.5)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0))(vitest@4.0.13)': + '@nx/vitest@22.3.3(@babel/traverse@7.28.0)(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17)))(typescript@5.9.3)(vite@7.1.9(@types/node@22.16.5)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0))(vitest@4.0.13(@types/node@22.16.5)(@vitest/ui@4.0.13)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0))': dependencies: '@nx/devkit': 22.3.3(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))) '@nx/js': 22.3.3(@babel/traverse@7.28.0)(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))(nx@22.3.3(@swc-node/register@1.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@swc/types@0.1.24)(typescript@5.9.3))(@swc/core@1.13.5(@swc/helpers@0.5.17))) @@ -9476,7 +9437,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitest/coverage-v8@4.0.13(vitest@4.0.13)': + '@vitest/coverage-v8@4.0.13(vitest@4.0.13(@types/node@22.16.5)(@vitest/ui@4.0.13)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.86.0)(sass@1.86.0)(terser@5.39.1)(yaml@2.8.0))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.13