diff --git a/.gitignore b/.gitignore index f2471a36d3..9ba4ef9ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ postgres/pgsql-test/output/ .env.local graphql/server/logs/ graphql/server/*.heapsnapshot +graphql/server/perf/results/ diff --git a/graphile/graphile-multi-tenancy-cache/README.md b/graphile/graphile-multi-tenancy-cache/README.md new file mode 100644 index 0000000000..253cb98390 --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/README.md @@ -0,0 +1,248 @@ +# graphile-multi-tenancy-cache + +

+ +

+ +

+ + + + + +

+ +Multi-tenancy cache utilities for PostGraphile. This package implements exact-match buildKey-based handler reuse for Constructive's GraphQL server runtime. + +The runtime model is intentionally conservative: + +- reuse handlers only when build inputs match exactly +- no template sharing +- no SQL rewrite +- no fingerprint-based handler reuse + +## Table of contents + +- [Installation](#installation) +- [Usage](#usage) +- [Features](#features) +- [Core concepts](#core-concepts) +- [How the handler cache works](#how-the-handler-cache-works) +- [API](#api) +- [License](#license) + +## Installation + +```bash +npm install graphile-multi-tenancy-cache +``` + +## Usage + +This package is a runtime orchestrator, not a schema plugin. You configure it with a handler factory, then resolve handlers per request. + +```typescript +import { + configureMultiTenancyCache, + getTenantInstance, + getOrCreateTenantInstance, + flushTenantInstance, + shutdownMultiTenancyCache, +} from 'graphile-multi-tenancy-cache'; + +configureMultiTenancyCache({ + async handlerFactory({ + buildKey, + svcKey, + pool, + schemas, + anonRole, + roleName, + databaseId, + presetOptions, + }) { + return createTenantGraphileResources({ + pool, + schemas, + anonRole, + roleName, + databaseSettings: presetOptions, + cacheKey: buildKey, + serviceKey: svcKey, + databaseId, + }); + }, +}); + +async function handleGraphql(req, res) { + const svcKey = req.svc_key; + + let tenant = getTenantInstance(svcKey); + if (!tenant) { + tenant = await getOrCreateTenantInstance({ + svcKey, + pool: req.pgPool, + schemas: req.api.schema, + anonRole: req.api.anonRole, + roleName: req.api.roleName, + databaseId: req.api.databaseId, + presetOptions: req.api.databaseSettings, + }); + } + + tenant.handler(req, res); +} + +process.on('SIGTERM', async () => { + await shutdownMultiTenancyCache(); +}); +``` + +## Features + +- **Exact-match buildKey reuse** — handlers are shared only when connection identity, schema set, role inputs, and preset options match exactly +- **Request-key indirection** — `svc_key` remains the routing and flush key while `buildKey` becomes the handler identity +- **Single-flight creation** — concurrent requests for the same `buildKey` coalesce onto one in-flight handler build +- **Safe rebinding** — reassigning a `svc_key` to a new `buildKey` cleans up unreachable handlers and stale indexes +- **Targeted flush APIs** — evict by `svc_key` or by `databaseId` +- **Handler lifecycle management** — graceful disposal and full shutdown support +- **Diagnostics-friendly** — exposes cache stats and `svc_key -> buildKey` lookup helpers + +## Core concepts + +| Concept | Meaning | +|--------|---------| +| `svc_key` | Request routing key. Used to look up which cached handler the current request should hit. | +| `buildKey` | Handler identity. A canonical string computed from the inputs that materially affect Graphile instance construction. | +| `databaseId` | Metadata/flush key. Used to evict all handlers associated with a database. | + +### What goes into the buildKey + +`buildKey` is computed from: + +- connection identity +- schema list +- `anonRole` +- `roleName` +- `presetOptions`, when supplied + +It does **not** include: + +- `svc_key` +- `databaseId` +- request host/domain +- auth tokens or transient headers + +The value is stored as a canonical plain-text key rather than a truncated hash, so different build inputs cannot collide onto the same handler key. + +Schema order is preserved. `['a', 'b']` and `['b', 'a']` intentionally produce different buildKeys. + +Examples: + +- A buildKey is a canonical string derived from connection identity, schemas, role inputs, and preset options: + +```json +{"conn":"127.0.0.1:5432/mydb@postgres","schemas":["services_public"],"anonRole":"administrator","roleName":"administrator","presetOptions":{"watch":false}} +``` + +- Different route keys can still share the same handler when they resolve to the same build inputs: + +```text +svc_key: tenant-a.example.com +svc_key: tenant-b.example.com +svc_key: api:main-db:services-api +svc_key: schemata:main-db:services_public +``` + + Each route key is first resolved into the inputs that matter for handler construction: + + - `dbname` + - `schemas` + - `anonRole` + - `roleName` + - `presetOptions` + + These then feed into the buildKey: + +```json +{"conn":":/@","schemas":[...],"anonRole":"...","roleName":"...","presetOptions":{...}} +``` + + Different route keys only share a `buildKey` if they ultimately resolve to the same: + + - `conn` + - `schemas` + - `anonRole` + - `roleName` + - `presetOptions` + + In practice, the resolution rules differ by path: + + - domain lookup / `X-Api-Name` usually resolve roles from the API record + - `X-Schemata` uses administrator defaults and takes schemas directly from the header + + For example, `api:main-db:services-api` and `schemata:main-db:services_public` + only share a handler if the `services-api` lookup ultimately resolves to the + same schema list and the same role settings. In many deployments, they do not. + +- Schema order matters, so these produce different buildKeys: + +```json +{"conn":"127.0.0.1:5432/mydb@postgres","schemas":["services_public","metaschema_public"],"anonRole":"administrator","roleName":"administrator","presetOptions":{"watch":false}} +{"conn":"127.0.0.1:5432/mydb@postgres","schemas":["metaschema_public","services_public"],"anonRole":"administrator","roleName":"administrator","presetOptions":{"watch":false}} +``` + +- Different database connections also produce different buildKeys, even when schema names match. + +## How the handler cache works + +At runtime the cache maintains three main indexes: + +- `buildKey -> TenantInstance` +- `svc_key -> buildKey` +- `databaseId -> Set` + +The flow is: + +1. Compute the `buildKey` from pool identity, schemas, role inputs, and preset options. +2. Check the handler cache for an existing `buildKey`. +3. If another request is already building that handler, await the shared promise. +4. If no handler exists, create a fresh PostGraphile instance. +5. Register the `svc_key -> buildKey` mapping only after creation succeeds. + +This means: + +- different request keys can share one handler when build inputs are identical +- failed in-flight creation does not leave orphaned mappings +- stale `svc_key` rebindings can be evicted cleanly + +### Fast path vs slow path + +- **Fast path**: `svc_key -> buildKey -> TenantInstance` +- **Slow path**: compute `buildKey`, create/coalesce handler, then register mapping + +### Flush and shutdown + +The package supports: + +- flushing one routed tenant by `svc_key` +- flushing all handlers associated with a `databaseId` +- full shutdown and disposal of cached handlers + +## API + +| Export | Purpose | +|--------|---------| +| `configureMultiTenancyCache(config)` | Registers the handler factory. Must be called before handler creation. | +| `getTenantInstance(svcKey)` | Fast-path lookup via `svc_key`. | +| `getOrCreateTenantInstance(config)` | Resolve or create a handler for a request. | +| `flushTenantInstance(svcKey)` | Evict the handler currently mapped to a route key. | +| `flushByDatabaseId(databaseId)` | Evict all handlers associated with a database. | +| `getMultiTenancyCacheStats()` | Return cache/index counts for diagnostics. | +| `shutdownMultiTenancyCache()` | Dispose handlers and clear all internal state. | +| `computeBuildKey(pool, schemas, anonRole, roleName, presetOptions?)` | Compute the exact-match handler identity. | +| `getBuildKeyForSvcKey(svcKey)` | Resolve the buildKey currently mapped to a route key. | + +## License + +MIT diff --git a/graphile/graphile-multi-tenancy-cache/jest.config.js b/graphile/graphile-multi-tenancy-cache/jest.config.js new file mode 100644 index 0000000000..057a9420ed --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/jest.config.js @@ -0,0 +1,18 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + babelConfig: false, + tsconfig: 'tsconfig.json', + }, + ], + }, + transformIgnorePatterns: [`/node_modules/*`], + testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + modulePathIgnorePatterns: ['dist/*'] +}; diff --git a/graphile/graphile-multi-tenancy-cache/package.json b/graphile/graphile-multi-tenancy-cache/package.json new file mode 100644 index 0000000000..6ebdef79d5 --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/package.json @@ -0,0 +1,51 @@ +{ + "name": "graphile-multi-tenancy-cache", + "version": "0.1.0", + "author": "Constructive ", + "description": "Multi-tenancy cache utilities for PostGraphile — exact-match buildKey handler reuse", + "main": "index.js", + "module": "esm/index.js", + "types": "index.d.ts", + "homepage": "https://github.com/constructive-io/constructive", + "license": "MIT", + "publishConfig": { + "access": "public", + "directory": "dist" + }, + "repository": { + "type": "git", + "url": "https://github.com/constructive-io/constructive" + }, + "scripts": { + "clean": "makage clean", + "prepack": "npm run build", + "build": "makage build", + "build:dev": "makage build --dev", + "lint": "eslint . --fix", + "test": "jest", + "test:watch": "jest --watch" + }, + "keywords": [ + "postgraphile", + "graphile", + "multi-tenancy", + "cache", + "buildkey", + "constructive" + ], + "bugs": { + "url": "https://github.com/constructive-io/constructive/issues" + }, + "dependencies": { + "@pgpmjs/logger": "workspace:^", + "express": "^5.2.1", + "lru-cache": "^11.2.7", + "pg": "^8.11.3" + }, + "devDependencies": { + "@types/express": "^5.0.6", + "@types/pg": "^8.10.9", + "makage": "^0.3.0", + "ts-node": "^10.9.2" + } +} diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts new file mode 100644 index 0000000000..8c3ed53dbb --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts @@ -0,0 +1,1122 @@ +/** + * Unit tests for buildKey-based handler caching. + * + * Tests cover: + * - buildKey computation determinism and sensitivity + * - identical build inputs with different svc_keys share the same handler + * - different schemas / roles produce different buildKeys + * - svc_key-based flush evicts the correct handler + * - databaseId-level flush works correctly + * - shutdown clears all state + */ + +// We test computeBuildKey directly and use mocks for the orchestrator functions +// that depend on PostGraphile. + +// --- computeBuildKey tests (pure function, no mocking needed) --- + +import { computeBuildKey } from '../multi-tenancy-cache'; + +/** + * Create a mock Pool that matches the REAL pg-cache shape: + * `new pg.Pool({ connectionString })`. + * + * pg-cache's getPgPool() creates pools with connectionString only — + * individual fields (host, port, database, user) are NOT on pool.options. + * This mock must match that shape to test the real code path. + */ +function makeMockPool(overrides: { + host?: string; + port?: number; + database?: string; + user?: string; + password?: string; +} = {}): import('pg').Pool { + const host = overrides.host ?? 'localhost'; + const port = overrides.port ?? 5432; + const database = overrides.database ?? 'testdb'; + const user = overrides.user ?? 'postgres'; + const password = overrides.password ?? 'pass'; + const connectionString = `postgres://${user}:${password}@${host}:${port}/${database}`; + return { options: { connectionString } } as unknown as import('pg').Pool; +} + +describe('computeBuildKey', () => { + it('should be deterministic for identical inputs', () => { + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(pool, ['public'], 'anon', 'authenticated'); + expect(k1).toBe(k2); + }); + + it('should produce a canonical JSON string', () => { + const pool = makeMockPool(); + const key = computeBuildKey(pool, ['public'], 'anon', 'authenticated'); + expect(key).toBe( + JSON.stringify({ + conn: 'localhost:5432/testdb@postgres', + schemas: ['public'], + anonRole: 'anon', + roleName: 'authenticated', + }), + ); + }); + + it('should differ when schemas differ', () => { + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(pool, ['private'], 'anon', 'authenticated'); + expect(k1).not.toBe(k2); + }); + + it('should differ when schema order differs', () => { + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['public', 'private'], 'anon', 'authenticated'); + const k2 = computeBuildKey(pool, ['private', 'public'], 'anon', 'authenticated'); + expect(k1).not.toBe(k2); + }); + + it('should differ when anonRole differs', () => { + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(pool, ['public'], 'guest', 'authenticated'); + expect(k1).not.toBe(k2); + }); + + it('should differ when roleName differs', () => { + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(pool, ['public'], 'anon', 'admin'); + expect(k1).not.toBe(k2); + }); + + it('should differ when database differs', () => { + const p1 = makeMockPool({ database: 'db_a' }); + const p2 = makeMockPool({ database: 'db_b' }); + const k1 = computeBuildKey(p1, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(p2, ['public'], 'anon', 'authenticated'); + expect(k1).not.toBe(k2); + }); + + it('should differ when host differs', () => { + const p1 = makeMockPool({ host: 'host-a' }); + const p2 = makeMockPool({ host: 'host-b' }); + const k1 = computeBuildKey(p1, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(p2, ['public'], 'anon', 'authenticated'); + expect(k1).not.toBe(k2); + }); + + it('should differ when port differs', () => { + const p1 = makeMockPool({ port: 5432 }); + const p2 = makeMockPool({ port: 5433 }); + const k1 = computeBuildKey(p1, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(p2, ['public'], 'anon', 'authenticated'); + expect(k1).not.toBe(k2); + }); + + it('should differ when user differs', () => { + const p1 = makeMockPool({ user: 'alice' }); + const p2 = makeMockPool({ user: 'bob' }); + const k1 = computeBuildKey(p1, ['public'], 'anon', 'authenticated'); + const k2 = computeBuildKey(p2, ['public'], 'anon', 'authenticated'); + expect(k1).not.toBe(k2); + }); + + it('should NOT differ when only svc_key would differ (svc_key is not an input)', () => { + // Same pool, schemas, roles → same buildKey, regardless of svc_key + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['services_public'], 'administrator', 'administrator'); + const k2 = computeBuildKey(pool, ['services_public'], 'administrator', 'administrator'); + expect(k1).toBe(k2); + }); + + it('should differ when preset options differ', () => { + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['public'], 'anon', 'auth', { enableSearch: true }); + const k2 = computeBuildKey(pool, ['public'], 'anon', 'auth', { enableSearch: false }); + expect(k1).not.toBe(k2); + }); + + it('should canonicalize preset option object key order', () => { + const pool = makeMockPool(); + const k1 = computeBuildKey(pool, ['public'], 'anon', 'auth', { + enableSearch: true, + enableRealtime: false, + }); + const k2 = computeBuildKey(pool, ['public'], 'anon', 'auth', { + enableRealtime: false, + enableSearch: true, + }); + expect(k1).toBe(k2); + }); +}); + +jest.mock('@pgpmjs/logger', () => ({ + Logger: jest.fn().mockImplementation(() => ({ + info: jest.fn(), + debug: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + })), +})); + +import { + configureMultiTenancyCache, + getOrCreateTenantInstance, + getTenantInstance, + flushTenantInstance, + flushByDatabaseId, + getMultiTenancyCacheStats, + shutdownMultiTenancyCache, + getBuildKeyForSvcKey, + type TenantHandlerFactoryContext, + type TenantHandlerResources, +} from '../multi-tenancy-cache'; + +const makeMockResources = (_ctx: TenantHandlerFactoryContext): TenantHandlerResources => ({ + handler: jest.fn() as never, + release: jest.fn(async () => {}), +}); + +const mockHandlerFactory = jest.fn(async (ctx: TenantHandlerFactoryContext) => makeMockResources(ctx)); + +beforeEach(async () => { + await shutdownMultiTenancyCache(); + configureMultiTenancyCache({ handlerFactory: mockHandlerFactory }); + mockHandlerFactory.mockClear(); +}); + +afterAll(async () => { + await shutdownMultiTenancyCache(); +}); + +describe('getOrCreateTenantInstance — buildKey deduplication', () => { + it('should return same handler for different svc_keys with identical build inputs', async () => { + const pool = makeMockPool(); + + const t1 = await getOrCreateTenantInstance({ + svcKey: 'schemata:db-0001-tenant-a:services_public', + pool, + schemas: ['services_public'], + anonRole: 'administrator', + roleName: 'administrator', + }); + + const t2 = await getOrCreateTenantInstance({ + svcKey: 'schemata:db-0002-tenant-b:services_public', + pool, + schemas: ['services_public'], + anonRole: 'administrator', + roleName: 'administrator', + }); + + // Same handler object (same buildKey) + expect(t1).toBe(t2); + expect(t1.buildKey).toBe(t2.buildKey); + + // Handler factory called only once (deduplication) + expect(mockHandlerFactory).toHaveBeenCalledTimes(1); + + // Both svc_keys resolve to the same buildKey + expect(getBuildKeyForSvcKey('schemata:db-0001-tenant-a:services_public')).toBe(t1.buildKey); + expect(getBuildKeyForSvcKey('schemata:db-0002-tenant-b:services_public')).toBe(t1.buildKey); + }); + + it('should return different handlers when schemas differ', async () => { + const pool = makeMockPool(); + + const t1 = await getOrCreateTenantInstance({ + svcKey: 'tenant-a', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + }); + + const t2 = await getOrCreateTenantInstance({ + svcKey: 'tenant-b', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + }); + + expect(t1).not.toBe(t2); + expect(t1.buildKey).not.toBe(t2.buildKey); + expect(mockHandlerFactory).toHaveBeenCalledTimes(2); + }); + + it('should return different handlers when roles differ', async () => { + const pool = makeMockPool(); + + const t1 = await getOrCreateTenantInstance({ + svcKey: 'tenant-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'user', + }); + + const t2 = await getOrCreateTenantInstance({ + svcKey: 'tenant-b', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'admin', + }); + + expect(t1).not.toBe(t2); + expect(t1.buildKey).not.toBe(t2.buildKey); + }); + + it('should return different handlers when preset options differ', async () => { + const pool = makeMockPool(); + + const t1 = await getOrCreateTenantInstance({ + svcKey: 'tenant-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + presetOptions: { enableSearch: true }, + }); + + const t2 = await getOrCreateTenantInstance({ + svcKey: 'tenant-b', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + presetOptions: { enableSearch: false }, + }); + + expect(t1).not.toBe(t2); + expect(t1.buildKey).not.toBe(t2.buildKey); + expect(mockHandlerFactory).toHaveBeenCalledTimes(2); + }); +}); + +describe('getTenantInstance — fast path', () => { + it('should return handler after registration via getOrCreateTenantInstance', async () => { + const pool = makeMockPool(); + await getOrCreateTenantInstance({ + svcKey: 'key-1', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + }); + + const result = getTenantInstance('key-1'); + expect(result).toBeDefined(); + expect(result!.buildKey).toBeTruthy(); + }); + + it('should return undefined for unregistered svc_key', () => { + expect(getTenantInstance('nonexistent')).toBeUndefined(); + }); +}); + +describe('flushTenantInstance — svc_key-based flush', () => { + it('should evict the handler and clear all svc_key mappings for the buildKey', async () => { + const pool = makeMockPool(); + + // Two svc_keys share the same handler + await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + }); + await getOrCreateTenantInstance({ + svcKey: 'key-b', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + }); + + expect(getTenantInstance('key-a')).toBeDefined(); + expect(getTenantInstance('key-b')).toBeDefined(); + + // Flush via key-a + flushTenantInstance('key-a'); + + // Both svc_keys should lose their handler (same buildKey was evicted) + expect(getTenantInstance('key-a')).toBeUndefined(); + expect(getTenantInstance('key-b')).toBeUndefined(); + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(0); + expect(stats.svcKeyMappings).toBe(0); + }); + + it('should not affect handlers with different buildKeys', async () => { + const pool = makeMockPool(); + + await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + }); + await getOrCreateTenantInstance({ + svcKey: 'key-b', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + }); + + flushTenantInstance('key-a'); + + expect(getTenantInstance('key-a')).toBeUndefined(); + expect(getTenantInstance('key-b')).toBeDefined(); + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(1); + }); + + it('should be a no-op for unknown svc_key', () => { + expect(() => flushTenantInstance('nonexistent')).not.toThrow(); + }); +}); + +describe('flushByDatabaseId — database-level flush', () => { + it('should evict all handlers associated with a databaseId', async () => { + const pool = makeMockPool(); + + await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + await getOrCreateTenantInstance({ + svcKey: 'key-b', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(2); + + flushByDatabaseId('db-001'); + + expect(getTenantInstance('key-a')).toBeUndefined(); + expect(getTenantInstance('key-b')).toBeUndefined(); + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(0); + expect(getMultiTenancyCacheStats().databaseIdMappings).toBe(0); + }); + + it('should not affect handlers from other databaseIds', async () => { + const pool = makeMockPool(); + + await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + await getOrCreateTenantInstance({ + svcKey: 'key-b', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-002', + }); + + flushByDatabaseId('db-001'); + + expect(getTenantInstance('key-a')).toBeUndefined(); + expect(getTenantInstance('key-b')).toBeDefined(); + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(1); + }); + + it('should be a no-op for unknown databaseId', () => { + expect(() => flushByDatabaseId('nonexistent')).not.toThrow(); + }); +}); + +describe('shutdownMultiTenancyCache', () => { + it('should clear all state', async () => { + const pool = makeMockPool(); + + await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + await getOrCreateTenantInstance({ + svcKey: 'key-b', + pool, + schemas: ['private'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-002', + }); + + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(2); + expect(getMultiTenancyCacheStats().svcKeyMappings).toBe(2); + + await shutdownMultiTenancyCache(); + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(0); + expect(stats.svcKeyMappings).toBe(0); + expect(stats.databaseIdMappings).toBe(0); + expect(stats.inflightCreations).toBe(0); + }); + + it('should release each handler only once', async () => { + const pool = makeMockPool(); + + const tenant = await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + const release = tenant.release as jest.Mock; + + await shutdownMultiTenancyCache(); + + expect(release).toHaveBeenCalledTimes(1); + }); +}); + +describe('getMultiTenancyCacheStats', () => { + it('should report correct counts', async () => { + const pool = makeMockPool(); + + // Create 3 svc_keys, 2 of which share the same buildKey + await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + await getOrCreateTenantInstance({ + svcKey: 'key-b', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + await getOrCreateTenantInstance({ + svcKey: 'key-c', + pool, + schemas: ['private'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-002', + }); + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(2); // 2 unique buildKeys + expect(stats.svcKeyMappings).toBe(3); // 3 svc_keys + expect(stats.databaseIdMappings).toBe(2); // 2 databaseIds + expect(stats.inflightCreations).toBe(0); + }); +}); + +describe('re-creation after flush', () => { + it('should create a new handler after flushing and re-requesting', async () => { + const pool = makeMockPool(); + + const t1 = await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + }); + + flushTenantInstance('key-a'); + expect(getTenantInstance('key-a')).toBeUndefined(); + + const t2 = await getOrCreateTenantInstance({ + svcKey: 'key-a', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + }); + + // New handler instance (though same buildKey) + expect(t2).not.toBe(t1); + expect(t2.buildKey).toBe(t1.buildKey); + expect(getTenantInstance('key-a')).toBeDefined(); + }); +}); + +describe('handler creation failure — orphaned index cleanup', () => { + it('should clean up svc_key index when handler creation fails', async () => { + // Make the handler factory throw to simulate handler creation failure + const failingFactory = jest.fn(async () => { + throw new Error('simulated build failure'); + }); + configureMultiTenancyCache({ handlerFactory: failingFactory }); + + const pool = makeMockPool(); + + await expect( + getOrCreateTenantInstance({ + svcKey: 'failing-key', + pool, + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-fail', + }), + ).rejects.toThrow('simulated build failure'); + + // svc_key index should NOT have orphaned entries + expect(getBuildKeyForSvcKey('failing-key')).toBeUndefined(); + + // Stats should show clean state + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(0); + expect(stats.svcKeyMappings).toBe(0); + expect(stats.databaseIdMappings).toBe(0); + }); + + it('should not affect other svc_keys when one fails', async () => { + const pool = makeMockPool(); + + // First, create a successful handler + const t1 = await getOrCreateTenantInstance({ + svcKey: 'good-key', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }); + + expect(getTenantInstance('good-key')).toBeDefined(); + + // Now make the next creation fail (different buildKey — different schemas) + const failingFactory = jest.fn(async () => { + throw new Error('simulated build failure'); + }); + configureMultiTenancyCache({ handlerFactory: failingFactory }); + + await expect( + getOrCreateTenantInstance({ + svcKey: 'bad-key', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-001', + }), + ).rejects.toThrow('simulated build failure'); + + // good-key should still work + expect(getTenantInstance('good-key')).toBeDefined(); + expect(getTenantInstance('good-key')!.buildKey).toBe(t1.buildKey); + + // bad-key should be cleaned up + expect(getBuildKeyForSvcKey('bad-key')).toBeUndefined(); + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(1); + expect(stats.svcKeyMappings).toBe(1); + }); +}); + +describe('connectionString-based pool identity', () => { + it('should produce different buildKeys for pools with different connectionStrings', () => { + // This is the REAL production scenario — pools created via pg-cache's getPgPool + // have { options: { connectionString } }, not { options: { host, database, user } } + const poolA = { options: { connectionString: 'postgres://user:pass@host-a:5432/db_a' } } as unknown as import('pg').Pool; + const poolB = { options: { connectionString: 'postgres://user:pass@host-b:5432/db_b' } } as unknown as import('pg').Pool; + + const keyA = computeBuildKey(poolA, ['public'], 'anon', 'auth'); + const keyB = computeBuildKey(poolB, ['public'], 'anon', 'auth'); + + expect(keyA).not.toBe(keyB); + }); + + it('should produce the same buildKey for identical connectionStrings', () => { + const pool1 = { options: { connectionString: 'postgres://user:pass@host:5432/mydb' } } as unknown as import('pg').Pool; + const pool2 = { options: { connectionString: 'postgres://user:pass@host:5432/mydb' } } as unknown as import('pg').Pool; + + const key1 = computeBuildKey(pool1, ['public'], 'anon', 'auth'); + const key2 = computeBuildKey(pool2, ['public'], 'anon', 'auth'); + + expect(key1).toBe(key2); + }); + + it('should differ when only database name differs in connectionString', () => { + const pool1 = { options: { connectionString: 'postgres://user:pass@host:5432/db_alpha' } } as unknown as import('pg').Pool; + const pool2 = { options: { connectionString: 'postgres://user:pass@host:5432/db_beta' } } as unknown as import('pg').Pool; + + const key1 = computeBuildKey(pool1, ['public'], 'anon', 'auth'); + const key2 = computeBuildKey(pool2, ['public'], 'anon', 'auth'); + + expect(key1).not.toBe(key2); + }); + + it('should not include password in identity (password changes should not change buildKey)', () => { + // Both pools connect to the same host/db/user, only password differs + const pool1 = { options: { connectionString: 'postgres://user:pass1@host:5432/mydb' } } as unknown as import('pg').Pool; + const pool2 = { options: { connectionString: 'postgres://user:pass2@host:5432/mydb' } } as unknown as import('pg').Pool; + + const key1 = computeBuildKey(pool1, ['public'], 'anon', 'auth'); + const key2 = computeBuildKey(pool2, ['public'], 'anon', 'auth'); + + // buildKeys should be identical — password doesn't affect handler construction + expect(key1).toBe(key2); + }); + + it('should handle pools with individual fields (fallback path)', () => { + // Some consumers might create pools with explicit fields instead of connectionString + const pool = { options: { host: 'myhost', port: 5432, database: 'mydb', user: 'myuser' } } as unknown as import('pg').Pool; + const key = computeBuildKey(pool, ['public'], 'anon', 'auth'); + expect(key).toBe( + JSON.stringify({ + conn: 'myhost:5432/mydb@myuser', + schemas: ['public'], + anonRole: 'anon', + roleName: 'auth', + }), + ); + }); +}); + +// --- Finding 1: Coalesced creation failure leaves no orphaned mappings --- + +describe('coalesced creation failure — no orphaned mappings (Finding 1)', () => { + it('should clean up both svc_keys when 2 coalesced requests fail', async () => { + const failingFactory = jest.fn(async () => { throw new Error('coalesced fail'); }); + configureMultiTenancyCache({ handlerFactory: failingFactory }); + + const pool = makeMockPool(); + const base = { pool, schemas: ['public'] as string[], anonRole: 'anon', roleName: 'auth', databaseId: 'db-001' }; + + // Start two concurrent requests (same buildKey) — both will fail + const p1 = getOrCreateTenantInstance({ ...base, svcKey: 'coal-a' }); + const p2 = getOrCreateTenantInstance({ ...base, svcKey: 'coal-b' }); + + const results = await Promise.allSettled([p1, p2]); + expect(results[0].status).toBe('rejected'); + expect(results[1].status).toBe('rejected'); + + // No orphaned mappings for either svc_key + expect(getBuildKeyForSvcKey('coal-a')).toBeUndefined(); + expect(getBuildKeyForSvcKey('coal-b')).toBeUndefined(); + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(0); + expect(stats.svcKeyMappings).toBe(0); + expect(stats.databaseIdMappings).toBe(0); + expect(stats.inflightCreations).toBe(0); + }); + + it('should clean up all svc_keys when 3+ coalesced requests fail', async () => { + const failingFactory = jest.fn(async () => { throw new Error('coalesced fail 3+'); }); + configureMultiTenancyCache({ handlerFactory: failingFactory }); + + const pool = makeMockPool(); + const base = { pool, schemas: ['public'] as string[], anonRole: 'anon', roleName: 'auth', databaseId: 'db-002' }; + + // Start 4 concurrent requests with the same buildKey + const promises = [ + getOrCreateTenantInstance({ ...base, svcKey: 'coal-1' }), + getOrCreateTenantInstance({ ...base, svcKey: 'coal-2' }), + getOrCreateTenantInstance({ ...base, svcKey: 'coal-3' }), + getOrCreateTenantInstance({ ...base, svcKey: 'coal-4' }), + ]; + + const results = await Promise.allSettled(promises); + expect(results.every(r => r.status === 'rejected')).toBe(true); + + // No orphaned mappings for any svc_key + for (const key of ['coal-1', 'coal-2', 'coal-3', 'coal-4']) { + expect(getBuildKeyForSvcKey(key)).toBeUndefined(); + } + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(0); + expect(stats.svcKeyMappings).toBe(0); + expect(stats.databaseIdMappings).toBe(0); + expect(stats.inflightCreations).toBe(0); + }); + + it('should preserve all svc_key mappings when coalesced creation succeeds', async () => { + // Uses the default (working) handler factory from beforeEach + const pool = makeMockPool(); + const base = { pool, schemas: ['public'] as string[], anonRole: 'anon', roleName: 'auth', databaseId: 'db-003' }; + + // Start 3 concurrent requests (same buildKey) + const p1 = getOrCreateTenantInstance({ ...base, svcKey: 'ok-1' }); + const p2 = getOrCreateTenantInstance({ ...base, svcKey: 'ok-2' }); + const p3 = getOrCreateTenantInstance({ ...base, svcKey: 'ok-3' }); + + const [r1, r2, r3] = await Promise.all([p1, p2, p3]); + + // All got the same handler instance + expect(r1).toBe(r2); + expect(r2).toBe(r3); + expect(r1.buildKey).toBe(r2.buildKey); + + // All 3 svc_key mappings exist + expect(getBuildKeyForSvcKey('ok-1')).toBe(r1.buildKey); + expect(getBuildKeyForSvcKey('ok-2')).toBe(r1.buildKey); + expect(getBuildKeyForSvcKey('ok-3')).toBe(r1.buildKey); + + const stats = getMultiTenancyCacheStats(); + expect(stats.handlerCacheSize).toBe(1); + expect(stats.svcKeyMappings).toBe(3); + expect(stats.inflightCreations).toBe(0); + }); +}); + +// --- Finding 2: svc_key rebinding cleans up old handler --- + +describe('svc_key rebinding — old handler cleanup (Finding 2)', () => { + it('should evict old buildKey when rebound svc_key was its only reference', async () => { + const pool = makeMockPool(); + + // Create handler with schema_a + const tA = await getOrCreateTenantInstance({ + svcKey: 'rebind-key', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-r1', + }); + const buildKeyA = tA.buildKey; + + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(1); + + // Rebind the same svc_key to a different buildKey (different schemas) + const tB = await getOrCreateTenantInstance({ + svcKey: 'rebind-key', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-r1', + }); + const buildKeyB = tB.buildKey; + expect(buildKeyA).not.toBe(buildKeyB); + + // Old buildKey A should be evicted (no remaining svc_key references) + expect(getBuildKeyForSvcKey('rebind-key')).toBe(buildKeyB); + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(1); // only B remains + expect(getTenantInstance('rebind-key')).toBe(tB); + }); + + it('should NOT evict old buildKey if another svc_key still references it', async () => { + const pool = makeMockPool(); + + // Two svc_keys share the same buildKey (identical build inputs) + await getOrCreateTenantInstance({ + svcKey: 'shared-1', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + }); + const tShared = await getOrCreateTenantInstance({ + svcKey: 'shared-2', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + }); + const sharedBuildKey = tShared.buildKey; + + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(1); + expect(getMultiTenancyCacheStats().svcKeyMappings).toBe(2); + + // Rebind shared-1 to a different buildKey (different schemas) + const tNew = await getOrCreateTenantInstance({ + svcKey: 'shared-1', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + }); + + // Old buildKey should still exist — shared-2 still references it + expect(getBuildKeyForSvcKey('shared-1')).toBe(tNew.buildKey); + expect(getBuildKeyForSvcKey('shared-2')).toBe(sharedBuildKey); + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(2); // old + new + expect(getTenantInstance('shared-2')).toBe(tShared); + }); + + it('should keep databaseIdToBuildKeys consistent after rebinding', async () => { + const pool = makeMockPool(); + + // Create with databaseId + await getOrCreateTenantInstance({ + svcKey: 'db-key', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-x', + }); + + expect(getMultiTenancyCacheStats().databaseIdMappings).toBe(1); + + // Rebind to different schemas (different buildKey), same databaseId + await getOrCreateTenantInstance({ + svcKey: 'db-key', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-x', + }); + + // Old buildKey evicted (no remaining refs), new buildKey under same databaseId + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(1); + expect(getMultiTenancyCacheStats().databaseIdMappings).toBe(1); + + // Flushing by databaseId should still work + flushByDatabaseId('db-x'); + expect(getTenantInstance('db-key')).toBeUndefined(); + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(0); + expect(getMultiTenancyCacheStats().databaseIdMappings).toBe(0); + }); + + it('should allow flush to work correctly after rebinding', async () => { + const pool = makeMockPool(); + + // Create handler A + await getOrCreateTenantInstance({ + svcKey: 'flush-key', + pool, + schemas: ['schema_a'], + anonRole: 'anon', + roleName: 'auth', + }); + + // Rebind to handler B + await getOrCreateTenantInstance({ + svcKey: 'flush-key', + pool, + schemas: ['schema_b'], + anonRole: 'anon', + roleName: 'auth', + }); + + // Flush via the rebound svc_key — should flush the NEW handler + flushTenantInstance('flush-key'); + expect(getTenantInstance('flush-key')).toBeUndefined(); + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(0); + expect(getMultiTenancyCacheStats().svcKeyMappings).toBe(0); + }); +}); + +// --------------------------------------------------------------------------- +// svc_key race condition tests +// --------------------------------------------------------------------------- + +describe('getOrCreateTenantInstance — svc_key race condition (epoch guard)', () => { + /** + * Install a gated handler factory. Each handler creation creates a new gate + * and blocks until that gate is resolved. + * Returns the ordered array of gates so the test can resolve them in + * any desired order. + */ + function installGatedFactory(): Array<{ resolve: () => void }> { + const gates: Array<{ resolve: () => void }> = []; + const gatedFactory = jest.fn(async (ctx: TenantHandlerFactoryContext) => { + let resolve!: () => void; + const promise = new Promise((r) => { + resolve = r; + }); + gates.push({ resolve }); + await promise; + return makeMockResources(ctx); + }); + configureMultiTenancyCache({ handlerFactory: gatedFactory }); + return gates; + } + + it('newer request finishes first — final mapping stays on newer buildKey', async () => { + const gates = installGatedFactory(); + const pool = makeMockPool(); + + // Start OLDER request (buildKey A — schemas=['schema_old']) + const pOld = getOrCreateTenantInstance({ + svcKey: 'race-svc', + pool, + schemas: ['schema_old'], + anonRole: 'anon', + roleName: 'auth', + }); + + // Start NEWER request (buildKey B — schemas=['schema_new']) + const pNew = getOrCreateTenantInstance({ + svcKey: 'race-svc', + pool, + schemas: ['schema_new'], + anonRole: 'anon', + roleName: 'auth', + }); + + // Two separate handler creations (different buildKeys → no coalescing) + expect(gates.length).toBe(2); + + // Resolve NEWER first + gates[1].resolve(); + const resultNew = await pNew; + expect(getBuildKeyForSvcKey('race-svc')).toBe(resultNew.buildKey); + + // Resolve OLDER (stale completion) + gates[0].resolve(); + await pOld; + + // Flush microtask queue for deferred orphan cleanup + await new Promise((r) => queueMicrotask(r)); + + // Final mapping MUST remain on the newer buildKey + expect(getBuildKeyForSvcKey('race-svc')).toBe(resultNew.buildKey); + }); + + it('older request finishes first — final mapping ends on newer buildKey', async () => { + const gates = installGatedFactory(); + const pool = makeMockPool(); + + const pOld = getOrCreateTenantInstance({ + svcKey: 'race-svc-2', + pool, + schemas: ['schema_old'], + anonRole: 'anon', + roleName: 'auth', + }); + + const pNew = getOrCreateTenantInstance({ + svcKey: 'race-svc-2', + pool, + schemas: ['schema_new'], + anonRole: 'anon', + roleName: 'auth', + }); + + expect(gates.length).toBe(2); + + // Resolve OLDER first — this is stale since newer epoch already exists + gates[0].resolve(); + await pOld; + + // Flush microtask queue + await new Promise((r) => queueMicrotask(r)); + + // Resolve NEWER + gates[1].resolve(); + const resultNew = await pNew; + + await new Promise((r) => queueMicrotask(r)); + + // Final mapping MUST be on the newer buildKey + expect(getBuildKeyForSvcKey('race-svc-2')).toBe(resultNew.buildKey); + }); + + it('no orphaned handler/index state remains after race', async () => { + const gates = installGatedFactory(); + const pool = makeMockPool(); + + const pOld = getOrCreateTenantInstance({ + svcKey: 'race-svc-3', + pool, + schemas: ['schema_old'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-race', + }); + + const pNew = getOrCreateTenantInstance({ + svcKey: 'race-svc-3', + pool, + schemas: ['schema_new'], + anonRole: 'anon', + roleName: 'auth', + databaseId: 'db-race', + }); + + expect(gates.length).toBe(2); + + // Resolve newer first, then older (worst-case for orphans) + gates[1].resolve(); + await pNew; + gates[0].resolve(); + await pOld; + + // Flush microtask queue for deferred orphan cleanup + await new Promise((r) => queueMicrotask(r)); + + const stats = getMultiTenancyCacheStats(); + + // Only 1 handler should remain (the newer one) + expect(stats.handlerCacheSize).toBe(1); + // Only 1 svc_key mapping + expect(stats.svcKeyMappings).toBe(1); + // No in-flight creations + expect(stats.inflightCreations).toBe(0); + // The surviving handler is reachable via the svc_key + expect(getTenantInstance('race-svc-3')).toBeDefined(); + expect(getBuildKeyForSvcKey('race-svc-3')).toBeDefined(); + }); + + it('same-buildKey coalescing still works with epoch tracking', async () => { + // Epoch mechanism must NOT break single-flight behavior when two + // different svc_keys compute the same buildKey. + const pool = makeMockPool(); + + const p1 = getOrCreateTenantInstance({ + svcKey: 'coalesce-A', + pool, + schemas: ['shared_schema'], + anonRole: 'anon', + roleName: 'auth', + }); + + const p2 = getOrCreateTenantInstance({ + svcKey: 'coalesce-B', + pool, + schemas: ['shared_schema'], + anonRole: 'anon', + roleName: 'auth', + }); + + const [r1, r2] = await Promise.all([p1, p2]); + + // Same handler (coalesced on identical buildKey) + expect(r1.buildKey).toBe(r2.buildKey); + expect(r1).toBe(r2); + + // Both svc_keys mapped + expect(getBuildKeyForSvcKey('coalesce-A')).toBe(r1.buildKey); + expect(getBuildKeyForSvcKey('coalesce-B')).toBe(r2.buildKey); + + // Single handler in cache + expect(getMultiTenancyCacheStats().handlerCacheSize).toBe(1); + expect(getMultiTenancyCacheStats().svcKeyMappings).toBe(2); + }); +}); diff --git a/graphile/graphile-multi-tenancy-cache/src/build-key.ts b/graphile/graphile-multi-tenancy-cache/src/build-key.ts new file mode 100644 index 0000000000..8b4162486c --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/build-key.ts @@ -0,0 +1,95 @@ +import { Logger } from '@pgpmjs/logger'; +import type { Pool } from 'pg'; + +import type { BuildKeyParts } from './types'; + +const log = new Logger('multi-tenancy-cache:build-key'); + +/** + * Derive the pool connection identity from a pg.Pool instance. + * + * Real pg.Pool instances created via `new Pool({ connectionString })` store + * only `{ connectionString }` in `pool.options` — the individual fields + * (host, port, database, user) are NOT parsed onto the options object. + * + * This function handles both shapes: + * 1. connectionString-based (production — via pg-cache's getPgPool) + * 2. individual fields (fallback for pools created with explicit fields) + */ +export function getPoolIdentity(pool: Pool): string { + const opts = (pool as unknown as { options: Record }).options || {}; + + if (typeof opts.connectionString === 'string') { + try { + const url = new URL(opts.connectionString); + const host = url.hostname || 'localhost'; + const port = url.port || '5432'; + const database = url.pathname.slice(1) || ''; + const user = decodeURIComponent(url.username || ''); + return `${host}:${port}/${database}@${user}`; + } catch { + return opts.connectionString; + } + } + + if (opts.host || opts.database || opts.user) { + return `${opts.host || 'localhost'}:${opts.port || 5432}/${opts.database || ''}@${opts.user || ''}`; + } + + log.warn('Pool has no connectionString or individual connection fields — buildKey may not be unique'); + return 'unknown-pool'; +} + +export function normalizeBuildInput(value: unknown): unknown { + if (Array.isArray(value)) { + return value.map(normalizeBuildInput); + } + if (value && typeof value === 'object') { + const input = value as Record; + const output: Record = {}; + for (const key of Object.keys(input).sort()) { + const normalized = normalizeBuildInput(input[key]); + if (normalized !== undefined) { + output[key] = normalized; + } + } + return output; + } + return value; +} + +/** + * Compute the buildKey from the inputs that materially affect + * Graphile handler construction. + * + * Includes: + * - connection identity (host:port/database@user) + * - schemas (order preserved — NOT sorted) + * - anonRole + * - roleName + * - presetOptions/databaseSettings + * + * Does NOT include: + * - svc_key (routing-only) + * - databaseId (metadata-only) + * - token data, host/domain, transient headers + */ +export function computeBuildKey( + pool: Pool, + schemas: string[], + anonRole: string, + roleName: string, + presetOptions?: unknown, +): string { + const input: BuildKeyParts = { + conn: getPoolIdentity(pool), + schemas, + anonRole, + roleName, + }; + const normalizedPresetOptions = normalizeBuildInput(presetOptions); + if (normalizedPresetOptions !== undefined) { + input.presetOptions = normalizedPresetOptions; + } + return JSON.stringify(input); +} diff --git a/graphile/graphile-multi-tenancy-cache/src/cache-runtime.ts b/graphile/graphile-multi-tenancy-cache/src/cache-runtime.ts new file mode 100644 index 0000000000..054954a582 --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/cache-runtime.ts @@ -0,0 +1,234 @@ +import { Logger } from '@pgpmjs/logger'; +import { LRUCache } from 'lru-cache'; + +import { computeBuildKey } from './build-key'; +import { disposeTenant } from './lifecycle'; +import { TenantIndexes } from './tenant-indexes'; +import type { + HandlerCacheConfig, + MultiTenancyCacheConfig, + MultiTenancyCacheStats, + TenantConfig, + TenantHandlerFactory, + TenantHandlerFactoryContext, + TenantInstance, +} from './types'; + +const log = new Logger('multi-tenancy-cache'); + +const parsePositiveIntEnv = (value: string | undefined, fallback: number): number => { + const parsed = Number.parseInt(String(value ?? ''), 10); + return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; +}; + +const ONE_HOUR_MS = 1000 * 60 * 60; +const FIVE_MINUTES_MS = 1000 * 60 * 5; +const ONE_YEAR_MS = ONE_HOUR_MS * 24 * 366; + +export const getHandlerCacheConfig = (): HandlerCacheConfig => { + const isDevelopment = process.env.NODE_ENV === 'development'; + return { + max: parsePositiveIntEnv( + process.env.GRAPHILE_MULTI_TENANCY_CACHE_MAX ?? process.env.GRAPHILE_CACHE_MAX, + 50, + ), + ttlMs: parsePositiveIntEnv( + process.env.GRAPHILE_MULTI_TENANCY_CACHE_TTL_MS ?? process.env.GRAPHILE_CACHE_TTL_MS, + isDevelopment ? FIVE_MINUTES_MS : ONE_YEAR_MS, + ), + }; +}; + +export class CacheRuntime { + private readonly cacheConfig: HandlerCacheConfig; + private readonly handlerCache: LRUCache; + private readonly indexes = new TenantIndexes(); + private readonly creatingHandlers = new Map>(); + private readonly svcKeyEpoch = new Map(); + private handlerFactory: TenantHandlerFactory | null = null; + + constructor(cacheConfig: HandlerCacheConfig = getHandlerCacheConfig()) { + this.cacheConfig = cacheConfig; + this.handlerCache = new LRUCache({ + max: cacheConfig.max, + ttl: cacheConfig.ttlMs, + updateAgeOnGet: true, + dispose: (handler, buildKey) => { + this.indexes.removeBuildKey(buildKey); + disposeTenant(handler).catch((err) => { + log.error(`Failed to dispose handler buildKey=${buildKey}:`, err); + }); + }, + }); + } + + configure(config: MultiTenancyCacheConfig): void { + this.handlerFactory = config.handlerFactory; + log.info('Multi-tenancy cache configured (buildKey-based handler caching)'); + } + + getTenantInstance(svcKey: string): TenantInstance | undefined { + const buildKey = this.indexes.getBuildKeyForSvcKey(svcKey); + if (!buildKey) return undefined; + + const handler = this.handlerCache.get(buildKey); + if (handler) { + handler.lastUsedAt = Date.now(); + } + return handler; + } + + getBuildKeyForSvcKey(svcKey: string): string | undefined { + return this.indexes.getBuildKeyForSvcKey(svcKey); + } + + async getOrCreateTenantInstance(config: TenantConfig): Promise { + const { svcKey, pool, schemas, anonRole, roleName, databaseId, presetOptions } = config; + + if (!this.handlerFactory) { + throw new Error('Multi-tenancy cache not configured. Call configureMultiTenancyCache() first.'); + } + + const buildKey = computeBuildKey(pool, schemas, anonRole, roleName, presetOptions); + const epoch = (this.svcKeyEpoch.get(svcKey) ?? 0) + 1; + this.svcKeyEpoch.set(svcKey, epoch); + + const existing = this.handlerCache.get(buildKey); + if (existing) { + existing.lastUsedAt = Date.now(); + if (this.svcKeyEpoch.get(svcKey) === epoch) { + this.registerMapping(svcKey, buildKey, databaseId); + } + return existing; + } + + const pending = this.creatingHandlers.get(buildKey); + if (pending) { + const result = await pending; + if (this.svcKeyEpoch.get(svcKey) === epoch) { + this.registerMapping(svcKey, buildKey, databaseId); + } + return result; + } + + const promise = this.doCreateHandler({ + buildKey, + svcKey, + pool, + schemas, + anonRole, + roleName, + databaseId, + presetOptions, + }); + this.creatingHandlers.set(buildKey, promise); + + try { + const result = await promise; + if (this.svcKeyEpoch.get(svcKey) === epoch) { + this.registerMapping(svcKey, buildKey, databaseId); + } else { + queueMicrotask(() => { + if (this.indexes.getSvcKeysForBuildKey(buildKey).length === 0) { + this.evictBuildKey(buildKey); + } + }); + } + return result; + } finally { + this.creatingHandlers.delete(buildKey); + } + } + + flushTenantInstance(svcKey: string): void { + const buildKey = this.indexes.getBuildKeyForSvcKey(svcKey); + if (!buildKey) return; + + this.evictBuildKey(buildKey); + log.debug(`Flushed via svc_key=${svcKey} → buildKey=${buildKey}`); + } + + flushByDatabaseId(databaseId: string): void { + const keysToEvict = this.indexes.getBuildKeysForDatabaseId(databaseId); + if (keysToEvict.length === 0) return; + + for (const buildKey of keysToEvict) { + this.evictBuildKey(buildKey); + } + + this.indexes.deleteDatabaseId(databaseId); + log.debug(`Flushed ${keysToEvict.length} handler(s) for databaseId=${databaseId}`); + } + + getStats(): MultiTenancyCacheStats { + return { + handlerCacheSize: this.handlerCache.size, + handlerCacheMax: this.cacheConfig.max, + handlerCacheTtlMs: this.cacheConfig.ttlMs, + svcKeyMappings: this.indexes.svcKeyMappingCount, + databaseIdMappings: this.indexes.databaseIdMappingCount, + inflightCreations: this.creatingHandlers.size, + buildKeys: [...this.handlerCache.keys()], + }; + } + + async shutdown(): Promise { + log.info('Shutting down multi-tenancy cache...'); + + const disposals: Promise[] = []; + for (const handler of this.handlerCache.values()) { + disposals.push(disposeTenant(handler)); + } + await Promise.allSettled(disposals); + + this.handlerCache.clear(); + this.indexes.clear(); + this.creatingHandlers.clear(); + this.svcKeyEpoch.clear(); + this.handlerFactory = null; + + log.info('Multi-tenancy cache shutdown complete'); + } + + private registerMapping(svcKey: string, buildKey: string, databaseId?: string): void { + this.indexes.registerMapping( + svcKey, + buildKey, + databaseId, + (orphanedBuildKey) => this.evictBuildKey(orphanedBuildKey), + ); + } + + private evictBuildKey(buildKey: string): void { + const deleted = this.handlerCache.delete(buildKey); + if (!deleted) { + this.indexes.removeBuildKey(buildKey); + } + + log.debug(`Evicted buildKey=${buildKey} (handler=${deleted ? 'disposed' : 'none/orphaned'})`); + } + + private async doCreateHandler( + context: TenantHandlerFactoryContext, + ): Promise { + const { buildKey, schemas } = context; + const schemaLabel = schemas.join(',') || 'unknown'; + + log.info(`Building handler buildKey=${buildKey} schemas=${schemaLabel}`); + + const resources = await this.handlerFactory!(context); + + const tenant: TenantInstance = { + ...resources, + buildKey, + schemas, + createdAt: Date.now(), + lastUsedAt: Date.now(), + }; + + this.handlerCache.set(buildKey, tenant); + + log.info(`Handler created buildKey=${buildKey} schemas=${schemaLabel}`); + return tenant; + } +} diff --git a/graphile/graphile-multi-tenancy-cache/src/index.ts b/graphile/graphile-multi-tenancy-cache/src/index.ts new file mode 100644 index 0000000000..d5fedbb777 --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/index.ts @@ -0,0 +1,22 @@ +// --- Orchestrator (primary API) --- +export { + configureMultiTenancyCache, + getOrCreateTenantInstance, + getTenantInstance, + flushTenantInstance, + flushByDatabaseId, + getMultiTenancyCacheStats, + shutdownMultiTenancyCache, + computeBuildKey, + getBuildKeyForSvcKey, +} from './multi-tenancy-cache'; + +export type { + TenantConfig, + TenantHandlerFactory, + TenantHandlerFactoryContext, + TenantHandlerResources, + TenantInstance, + MultiTenancyCacheStats, + MultiTenancyCacheConfig, +} from './multi-tenancy-cache'; diff --git a/graphile/graphile-multi-tenancy-cache/src/lifecycle.ts b/graphile/graphile-multi-tenancy-cache/src/lifecycle.ts new file mode 100644 index 0000000000..2387018aaf --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/lifecycle.ts @@ -0,0 +1,35 @@ +import { Logger } from '@pgpmjs/logger'; + +import type { TenantInstance } from './types'; + +const log = new Logger('multi-tenancy-cache:lifecycle'); + +/** Tenant resources that have already been disposed. Prevents double release via manual + LRU paths. */ +const disposedTenants = new WeakSet(); + +export async function disposeTenant(tenant: TenantInstance): Promise { + if (disposedTenants.has(tenant)) { + return; + } + disposedTenants.add(tenant); + + try { + if (tenant.release) { + await tenant.release(); + return; + } + if (tenant.httpServer?.listening) { + await new Promise((resolve) => { + tenant.httpServer.close(() => resolve()); + }); + } + if (tenant.realtimeManager) { + await tenant.realtimeManager.stop(); + } + if (tenant.pgl) { + await tenant.pgl.release(); + } + } catch (err) { + log.error(`Error disposing handler buildKey=${tenant.buildKey}:`, err); + } +} diff --git a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts new file mode 100644 index 0000000000..a45788f3bc --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts @@ -0,0 +1,102 @@ +/** + * Multi-tenancy cache orchestrator. + * + * Caches one independent PostGraphile handler per **buildKey** (a canonical + * string derived from the inputs that materially affect Graphile handler + * construction). + * + * Multiple svc_key values with identical build inputs share the same handler. + * svc_key remains the request routing key and flush targeting key. + * + * No template sharing, no SQL rewrite, no fingerprinting. + * + * Index structures: + * handlerCache: buildKey → TenantInstance + * svcKeyToBuildKey: svc_key → buildKey + * databaseIdToBuildKeys: databaseId → Set + */ + +import { computeBuildKey } from './build-key'; +import { CacheRuntime } from './cache-runtime'; +import type { + MultiTenancyCacheConfig, + MultiTenancyCacheStats, + TenantConfig, + TenantInstance, +} from './types'; + +const store = new CacheRuntime(); + +/** + * One-time package bootstrap. Stores the handler factory. + * Must be called before any getOrCreateTenantInstance() calls. + */ +export function configureMultiTenancyCache(config: MultiTenancyCacheConfig): void { + store.configure(config); +} + +/** + * Fast-path lookup: svc_key → buildKey → handler. + */ +export function getTenantInstance(svcKey: string): TenantInstance | undefined { + return store.getTenantInstance(svcKey); +} + +/** + * Resolve the buildKey for a given svc_key (for diagnostics / external use). + */ +export function getBuildKeyForSvcKey(svcKey: string): string | undefined { + return store.getBuildKeyForSvcKey(svcKey); +} + +/** + * Resolve or create a tenant handler. + */ +export function getOrCreateTenantInstance(config: TenantConfig): Promise { + return store.getOrCreateTenantInstance(config); +} + +/** + * Flush by svc_key: resolve to buildKey, evict the handler. + * + * This removes the handler AND all svc_key mappings pointing to + * the same buildKey. Other svc_keys that shared the handler will + * re-create it on next request. + */ +export function flushTenantInstance(svcKey: string): void { + store.flushTenantInstance(svcKey); +} + +/** + * Flush all handlers associated with a databaseId. + */ +export function flushByDatabaseId(databaseId: string): void { + store.flushByDatabaseId(databaseId); +} + +/** + * Get diagnostic stats for the multi-tenancy cache system. + */ +export function getMultiTenancyCacheStats(): MultiTenancyCacheStats { + return store.getStats(); +} + +/** + * Release all resources — handler cache, indexes, and in-flight trackers. + */ +export function shutdownMultiTenancyCache(): Promise { + return store.shutdown(); +} + +export { computeBuildKey }; + +export type { + HandlerCacheConfig, + MultiTenancyCacheConfig, + MultiTenancyCacheStats, + TenantConfig, + TenantHandlerFactory, + TenantHandlerFactoryContext, + TenantHandlerResources, + TenantInstance, +} from './types'; diff --git a/graphile/graphile-multi-tenancy-cache/src/tenant-indexes.ts b/graphile/graphile-multi-tenancy-cache/src/tenant-indexes.ts new file mode 100644 index 0000000000..922702b5c3 --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/tenant-indexes.ts @@ -0,0 +1,76 @@ +export class TenantIndexes { + private readonly svcKeyToBuildKey = new Map(); + private readonly databaseIdToBuildKeys = new Map>(); + + get svcKeyMappingCount(): number { + return this.svcKeyToBuildKey.size; + } + + get databaseIdMappingCount(): number { + return this.databaseIdToBuildKeys.size; + } + + getBuildKeyForSvcKey(svcKey: string): string | undefined { + return this.svcKeyToBuildKey.get(svcKey); + } + + getSvcKeysForBuildKey(buildKey: string): string[] { + const result: string[] = []; + for (const [svcKey, bk] of this.svcKeyToBuildKey) { + if (bk === buildKey) result.push(svcKey); + } + return result; + } + + getBuildKeysForDatabaseId(databaseId: string): string[] { + const buildKeys = this.databaseIdToBuildKeys.get(databaseId); + return buildKeys ? [...buildKeys] : []; + } + + registerMapping( + svcKey: string, + buildKey: string, + databaseId: string | undefined, + evictOrphanedBuildKey: (buildKey: string) => void, + ): void { + const oldBuildKey = this.svcKeyToBuildKey.get(svcKey); + + if (oldBuildKey && oldBuildKey !== buildKey) { + this.svcKeyToBuildKey.delete(svcKey); + + if (this.getSvcKeysForBuildKey(oldBuildKey).length === 0) { + evictOrphanedBuildKey(oldBuildKey); + } + } + + this.svcKeyToBuildKey.set(svcKey, buildKey); + if (databaseId) { + let keys = this.databaseIdToBuildKeys.get(databaseId); + if (!keys) { + keys = new Set(); + this.databaseIdToBuildKeys.set(databaseId, keys); + } + keys.add(buildKey); + } + } + + removeBuildKey(buildKey: string): void { + for (const svcKey of this.getSvcKeysForBuildKey(buildKey)) { + this.svcKeyToBuildKey.delete(svcKey); + } + + for (const [dbId, keys] of this.databaseIdToBuildKeys) { + keys.delete(buildKey); + if (keys.size === 0) this.databaseIdToBuildKeys.delete(dbId); + } + } + + deleteDatabaseId(databaseId: string): void { + this.databaseIdToBuildKeys.delete(databaseId); + } + + clear(): void { + this.svcKeyToBuildKey.clear(); + this.databaseIdToBuildKeys.clear(); + } +} diff --git a/graphile/graphile-multi-tenancy-cache/src/types.ts b/graphile/graphile-multi-tenancy-cache/src/types.ts new file mode 100644 index 0000000000..f44ef9f690 --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/src/types.ts @@ -0,0 +1,70 @@ +import type { Express } from 'express'; +import type { Server as HttpServer } from 'node:http'; +import type { Pool } from 'pg'; + +export interface TenantConfig { + svcKey: string; + pool: Pool; + schemas: string[]; + anonRole: string; + roleName: string; + databaseId?: string; + presetOptions?: unknown; +} + +export interface TenantHandlerResources { + handler: Express; + httpServer?: HttpServer; + pgl?: { release(): void | PromiseLike }; + realtimeManager?: { stop(): Promise } | null; + release?: () => Promise; +} + +export interface TenantInstance extends TenantHandlerResources { + buildKey: string; + schemas: string[]; + createdAt: number; + lastUsedAt: number; +} + +export interface MultiTenancyCacheStats { + handlerCacheSize: number; + handlerCacheMax: number; + handlerCacheTtlMs: number; + svcKeyMappings: number; + databaseIdMappings: number; + inflightCreations: number; + buildKeys: string[]; +} + +export interface BuildKeyParts { + conn: string; + schemas: string[]; + anonRole: string; + roleName: string; + presetOptions?: unknown; +} + +export interface TenantHandlerFactoryContext { + buildKey: string; + svcKey: string; + pool: Pool; + schemas: string[]; + anonRole: string; + roleName: string; + databaseId?: string; + presetOptions?: unknown; +} + +export type TenantHandlerFactory = ( + context: TenantHandlerFactoryContext, +) => Promise; + +export interface MultiTenancyCacheConfig { + handlerFactory: TenantHandlerFactory; +} + +export interface HandlerCacheConfig { + max: number; + ttlMs: number; +} diff --git a/graphile/graphile-multi-tenancy-cache/tsconfig.esm.json b/graphile/graphile-multi-tenancy-cache/tsconfig.esm.json new file mode 100644 index 0000000000..02d148781f --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/tsconfig.esm.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "dist/esm", + "module": "nodenext" + } +} diff --git a/graphile/graphile-multi-tenancy-cache/tsconfig.json b/graphile/graphile-multi-tenancy-cache/tsconfig.json new file mode 100644 index 0000000000..c013e618cd --- /dev/null +++ b/graphile/graphile-multi-tenancy-cache/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "module": "nodenext", + "moduleResolution": "nodenext" + }, + "include": ["src/**/*"] +} diff --git a/graphql/env/src/env.ts b/graphql/env/src/env.ts index fdf8d62c29..bcb41b7f4a 100644 --- a/graphql/env/src/env.ts +++ b/graphql/env/src/env.ts @@ -27,6 +27,7 @@ export const getGraphQLEnvVars = (env: NodeJS.ProcessEnv = process.env): Partial API_ANON_ROLE, API_ROLE_NAME, API_DEFAULT_DATABASE_ID, + USE_MULTI_TENANCY_CACHE, EMBEDDER_PROVIDER, EMBEDDER_MODEL, @@ -57,6 +58,7 @@ export const getGraphQLEnvVars = (env: NodeJS.ProcessEnv = process.env): Partial ...(API_ANON_ROLE && { anonRole: API_ANON_ROLE }), ...(API_ROLE_NAME && { roleName: API_ROLE_NAME }), ...(API_DEFAULT_DATABASE_ID && { defaultDatabaseId: API_DEFAULT_DATABASE_ID }), + ...(USE_MULTI_TENANCY_CACHE && { useMultiTenancyCache: parseEnvBoolean(USE_MULTI_TENANCY_CACHE) }), }, ...((EMBEDDER_PROVIDER || CHAT_PROVIDER) && { llm: { diff --git a/graphql/server-test/package.json b/graphql/server-test/package.json index 32519e2a09..60cdf52f46 100644 --- a/graphql/server-test/package.json +++ b/graphql/server-test/package.json @@ -26,7 +26,8 @@ "build:dev": "makage build --dev", "lint": "eslint . --fix", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "test:perf": "jest --config perf/jest.config.js --runInBand" }, "devDependencies": { "@0no-co/graphql.web": "^1.2.0", diff --git a/graphql/server-test/perf/README.md b/graphql/server-test/perf/README.md new file mode 100644 index 0000000000..1a4adfeab8 --- /dev/null +++ b/graphql/server-test/perf/README.md @@ -0,0 +1,132 @@ +# graphql-server-test perf benchmarks + +This directory is for the new `graphql/server-test` performance benchmark rewrite. + +The confirmed implementation details live in [`SPEC.md`](./SPEC.md). This README and `SPEC.md` are the source of truth for the new implementation. Do not infer requirements from older perf code or compatibility scripts. + +## Status + +Design confirmed; implementation in progress under `graphql/server-test/perf`. + +The `constructive-local` DBPM strategy has been validated by an exploratory implementation. That code is preserved on the reference branch `perf/constructive-local-dbpm-reference` for implementation reference only. Future work should implement from this README and `SPEC.md`; the reference branch is not a source of additional requirements. + +## Confirmed decisions + +- The new perf benchmark path lives under `graphql/server-test/perf/`. +- This is a fresh rewrite, not a direct move of existing benchmark scripts. +- Jest will be used as the benchmark runner for parameterized, on-demand local runs. +- Benchmark servers will be started through `graphql-server-test`'s `getConnections()` helper. +- The `getConnections()` path should create the test database, start a real `@constructive-io/graphql-server` HTTP server, and provide teardown. +- These benchmarks are local / opt-in and must not run as part of the default `pnpm test` path. +- These benchmarks must not enter CI unless a future CI perf job explicitly opts in. +- The rewritten benchmark suite should include a matrix benchmark path with an internal preflight step. +- Preflight is part of the benchmark implementation, not a separately exposed command; it is only needed for public-routing cases. +- A benchmark run should be selected through config groups and optional config overlays that determine which matrix dimensions are exercised in that run. +- Benchmark execution must stay inside the new Jest / `getConnections()` implementation and must not shell out to external benchmark CLIs or shell scripts. +- The benchmark baseline seed is the `constructive-local` pgpm service from the companion `constructive-db` checkout. +- `constructive-local` must be resolved from the local repository layout or `PERF_CONSTRUCTIVE_LOCAL_PATH`; implementation must not hard-code a developer-specific absolute path. +- Public-route preflight uses a private GraphQL DBPM surface against the same test database to provision public tenants, hosts, and benchmark-owned business tables. +- Public measured load must go through real public host routing after preflight succeeds. + +## Benchmark scope + +The rewritten perf suite exposes one Jest benchmark entrypoint: the matrix benchmark. + +Preflight is not a standalone command. It is an internal implementation step used by public-routing matrix cases to prepare and validate fixture readiness, route profiles, and public host routing before load begins. + +The first matrix dimensions are: + +- `routingMode`: `private` / `public` +- `cacheMode`: `old` / `new` +- `scaleProfile`: a named `k` / duration / workers tuple +- `workloadProfile`: a named operation mix + +For the first version, `k` is a single shared scale value. Its concrete meaning depends on routing mode: + +- in `private` routing, `k` means private route/profile count; +- in `public` routing, `k` means public tenant/host/profile count. + +The default workload profiles are: + +- `private`: `metadata-read` for smoke and `cache-key-shared` for cache comparison matrices +- `public`: `business-crud`, a DBPM-backed business-table workload that runs create/getById/update/list traffic + +Private cache-key workloads: + +- `cache-key-shared`: creates `k` distinct private svc_key routes with identical Graphile build inputs, so the new multi-tenancy cache should dedupe them to one buildKey handler. +- `cache-key-distinct`: creates `k` distinct private svc_key routes with different schema combinations where possible, so old/new cache modes can be compared without buildKey dedupe. + +## Config groups + +A single benchmark run is selected through a config group. The config group determines which matrix dimensions are exercised. + +Initial config groups: + +| Config group | Routing modes | Cache modes | Scale | Purpose | +| --- | --- | --- | --- | --- | +| `smoke` | `private` | `new` | very small / short | Fast runner and private-route sanity check | +| `public-smoke` | `public` | `new` | very small / short | Fast public-route sanity check; runs internal public preflight | +| `private-cache-compare` | `private` | `old,new` | about 1 minute | Shared-buildKey old/new cache comparison for private routing | +| `private-cache-distinct` | `private` | `old,new` | about 1 minute | Distinct-buildKey old/new cache comparison for private routing | +| `k10-5min` | `private,public` | `old,new` | `k=10`, `duration=5min` | First full local benchmark matrix | + +Additional config groups can be added later, but they should not expand the default scope until the first matrix is stable. + +## Constructive-local DBPM strategy + +The perf suite uses `constructive-local` as the shared local baseline for both private and public benchmark cases. + +`getConnections()` must create the temporary test database, install `constructive-local`, start a real `@constructive-io/graphql-server` HTTP server, and provide teardown. The implementation may discover the companion `constructive-db/services/constructive-local` path by walking local ancestor/sibling repository directories, and it must allow an explicit `PERF_CONSTRUCTIVE_LOCAL_PATH` override for non-standard checkouts. + +For public-routing cases, preflight prepares the public profiles through GraphQL: + +- start with the public benchmark context from `getConnections()`; +- use a private admin GraphQL surface connected to the same test database; +- provision DBPM tenants/databases through private GraphQL; +- inspect generated services metadata through GraphQL; +- create benchmark-owned public business tables through GraphQL; +- introspect the generated public host schema; +- generate request profiles from the actual public host/table metadata; +- probe those profiles through public host routing before measured load begins. + +This private-then-public flow is part of public preflight. It is not a separate user-facing command and must not shell out to legacy DBPM or perf scripts. + +## Validation evidence + +The exploratory implementation validated these behaviors on June 29, 2026: + +- default Jest discovery did not include `perf/e2e-matrix.perf.ts`; +- perf unit tests passed; +- private smoke passed through the `constructive-local` private metadata routes; +- public smoke provisioned one DBPM public profile and completed measured load; +- public/new `k10-5min` provisioned 10 DBPM tenants, 10 public hosts, and 10 benchmark-owned business tables, then completed a 5 minute measured load with zero failed requests. + +These results are evidence for the selected design, not fixed performance thresholds. QPS and latency are observations unless `SPEC.md` adds explicit threshold behavior later. + +## Current public workload + +The public `business-crud` workload proves public host routing against benchmark-owned DBPM business tables and runs a weighted operation mix: + +- create benchmark rows; +- get rows by id; +- update rows by id; +- list recent rows; +- low-weight `__typename` sanity traffic. + +The public preflight introspects the generated table fields and mutation input types before load begins. If the generated public schema does not expose the expected list/create/update shape, the case fails through the normal route/load gates. + +## Cache size controls + +Cache max values can be set in JSON config overlays under `cacheSizes` or through env: + +- `PERF_GRAPHILE_CACHE_MAX` -> `GRAPHILE_CACHE_MAX` +- `PERF_MULTI_TENANCY_CACHE_MAX` -> `GRAPHILE_MULTI_TENANCY_CACHE_MAX` +- `PERF_PG_CACHE_MAX` -> `PG_CACHE_MAX` + +Direct `GRAPHILE_CACHE_MAX`, `GRAPHILE_MULTI_TENANCY_CACHE_MAX`, and `PG_CACHE_MAX` env values are also recorded. The resolved cache env is written to run summaries and case observations. + +## Target details + +The target preflight, matrix execution, file layout, fixture/setup strategy, parameter interface, report schema, and completeness contract are defined in [`SPEC.md`](./SPEC.md). + +There are no separate implementation phases in this README. The README and spec describe the target behavior for the AI implementation. diff --git a/graphql/server-test/perf/SPEC.md b/graphql/server-test/perf/SPEC.md new file mode 100644 index 0000000000..41b7fa55c3 --- /dev/null +++ b/graphql/server-test/perf/SPEC.md @@ -0,0 +1,967 @@ +# graphql-server-test perf benchmark spec + +This spec records the confirmed design for the new `graphql/server-test/perf` benchmark implementation. + +The implementation is centered on Jest and `getConnections()`. A benchmark run owns one or more managed benchmark contexts, where each context provides an isolated test database, a real `@constructive-io/graphql-server` HTTP server, request helpers, artifacts, and teardown. + +## Design boundary + +- `README.md` and this spec are the source of truth for the new implementation. +- Do not infer requirements from older perf code, compatibility scripts, or historical drafts. +- The public interface is a Jest benchmark runner selected by config groups and optional config overlays. +- Benchmark execution must stay inside this new Jest / `getConnections()` implementation. +- The implementation must not shell out to external benchmark CLIs or shell scripts. +- Preflight is not a standalone command. It is an internal step used by public-routing cases before measured load starts. +- The benchmark suite is local / opt-in and must not be part of the default test path or CI unless a future perf job explicitly opts in. +- Implementation must be derived from this spec and `README.md`. +- Exploratory implementation branches may be used as validation evidence and API examples, but they do not define additional requirements. + +## Reference implementation branch + +The exploratory implementation that validated the `constructive-local` DBPM strategy is preserved on branch: + +```text +perf/constructive-local-dbpm-reference +``` + +That branch is a technical reference for API shape and failure modes only. A new implementation should not infer requirements from it when this spec says otherwise. + +## Constructive-local baseline + +The benchmark baseline is the `constructive-local` pgpm service from the companion `constructive-db` checkout. + +Required behavior: + +- `getConnections()` must create an isolated temporary test database. +- Before the benchmark server starts, setup must install `constructive-local` through pgpm seed/bootstrap. +- After the server starts, setup and workload operations must use GraphQL where the product surface can express the operation. +- The implementation must not hard-code a developer-specific absolute path to `constructive-local`. +- Path resolution must support `PERF_CONSTRUCTIVE_LOCAL_PATH`. +- Without an explicit path override, path resolution should discover `constructive-db/services/constructive-local` from the local repository layout, for example by checking sibling or ancestor directories around the current `constructive` checkout. +- If `constructive-local` cannot be found, the benchmark must fail with an actionable error before measured load. + +The implementation must not fall back to older simple fixtures for the public benchmark matrix. SQL/bootstrap work is allowed only to install the baseline service before GraphQL can serve requests, or for benchmark-owned reset boundaries described later in this spec. + +## Matrix dimensions + +The first benchmark matrix expands these dimensions: + +- `routingMode`: `private` / `public` +- `cacheMode`: `old` / `new` +- `scaleProfile`: named `k` / duration / workers tuple +- `workloadProfile`: named operation mix + +For the first version, `k` is shared across routing modes: + +- in `private` routing, `k` means private route/profile count; +- in `public` routing, `k` means public tenant/host/profile count. + +Default workload profiles: + +- `private`: `metadata-read` for smoke; `cache-key-shared` for cache comparison matrices +- `public`: `business-crud`, a DBPM-backed business-table workload + +Private workload profiles: + +- `metadata-read`: reads constructive-local metadata routes. +- `cache-key-shared`: creates `k` distinct private svc_key routes with identical build inputs, so new cache mode can dedupe them to fewer buildKey handlers. +- `cache-key-distinct`: creates `k` distinct private svc_key routes with different schema combinations where possible, for no-dedupe cache comparison. + +Public workload profiles: + +- `business-crud`: runs create/getById/update/list traffic against the benchmark-owned DBPM table plus low-weight route sanity traffic. +- `business-read`: optional read-only public table workload for debugging. + +## Initial config groups + +| Config group | Routing modes | Cache modes | Scale | Purpose | +| --- | --- | --- | --- | --- | +| `smoke` | `private` | `new` | very small / short | Fast runner and private-route sanity check | +| `public-smoke` | `public` | `new` | very small / short | Fast public-route sanity check with internal preflight | +| `private-cache-compare` | `private` | `old,new` | about 1 minute | Shared-buildKey old/new cache comparison for private routing | +| `private-cache-distinct` | `private` | `old,new` | about 1 minute | Distinct-buildKey old/new cache comparison for private routing | +| `k10-5min` | `private,public` | `old,new` | `k=10`, `duration=5min` | First full local benchmark matrix | + +## 1. Public preflight spec + +Public-routing matrix cases must run preflight before measured load. + +Preflight is only required for `routingMode = public`. Private-routing cases may still run lightweight setup and probe logic, but that is normal case setup, not public preflight. + +### 1.1 Preflight inputs + +Public preflight receives: + +- the expanded matrix case; +- the active benchmark context; +- the selected scale profile, including `k`; +- the selected public workload profile; +- DBPM provision options derived from the config group/spec; +- the run artifact writer; +- hard-gate settings such as whether under-provisioning is allowed for tiny smoke runs. + +### 1.2 Preflight stages + +Public preflight should run these stages in order: + +1. **Config snapshot** + - Record the selected routing mode, cache mode, scale profile, workload profile, and provision options. + - Record the server base URL and context identifier. + +2. **DBPM provision** + - Provision the DBPM-backed tenants, APIs, public hosts, and business API/table metadata needed by the selected `k`. + - Use the `constructive-local` DBPM GraphQL surface through a private admin GraphQL route connected to the same temporary test database. + - Run inside the active benchmark context. + - Do not depend on a manually started external server. + - Do not expose DBPM provision as a standalone benchmark command. + +3. **Business workload preparation** + - Ensure the public business workload has benchmark-owned tables/data suitable for the selected operation mix. + - The `business-crud` workload must exercise create/getById/update/list traffic against the benchmark-owned table plus route sanity operations. + - The public GraphQL list/create/update shape must be discovered from schema introspection instead of hard-coded developer-local names. + - Prepare benchmark-owned schemas/tables/data only. + +4. **Access preparation** + - Prepare role/grant access required by the public workload. + - Limit access changes to benchmark-owned objects. + - Fail preflight if non-benchmark objects would be modified by default. + +5. **Profile generation** + - Generate public host-routing request profiles. + - Generate public business operation profiles for the selected workload profile. + - Preserve enough metadata to map every profile back to its tenant/API/host/table target. + +6. **Scale and keyspace validation** + - Validate provisioned public tenant/host/profile count against `k`. + - Validate route-key diversity/count against the selected scale profile. + - Tiny smoke groups may allow explicit under-provisioning only when the config says so. + +7. **Route probes** + - Run lightweight GraphQL requests before measured load. + - Validate HTTP success and zero unexpected GraphQL errors. + - Validate that host routing reaches the expected public API/table shape. + +8. **Artifact writing** + - Write a preflight report under the run directory. + - Include enough paths/counts/errors to debug why load did or did not start. + +### 1.3 DBPM provision helper + +DBPM provision is a sub-step of public preflight. + +Execution model: + +1. matrix runner expands a public case; +2. the case acquires a benchmark context from the context manager; +3. public preflight runs inside that context; +4. public preflight calls the DBPM provision helper; +5. provision output is converted into request/workload profiles; +6. route probes validate those profiles; +7. measured load starts only after preflight passes. + +The DBPM provision helper should be callable code used by preflight. It can have focused implementation tests, but benchmark users should not run it directly as the public benchmark interface. + +The validated `constructive-local` DBPM flow is: + +1. acquire a public benchmark context through `getConnections()`; +2. create or use a private admin GraphQL surface against the same test database and pg connection settings; +3. call private GraphQL DBPM provision, such as `createDatabaseProvisionModule`, to create a benchmark-owned tenant/database/public API host; +4. inspect generated service metadata through private GraphQL, including services and metaschema metadata; +5. identify the generated public `api` host and `app_public` schema for the provisioned tenant; +6. create a benchmark-owned business table through private GraphQL, such as `provisionTable`; +7. introspect the generated public host schema through public host routing; +8. derive public request profiles and operation metadata from the actual generated host/table shape. + +The private admin GraphQL surface is an internal preflight helper. It must use the same isolated temporary database as the public benchmark server. It must not be a manually started external service and must not become a public benchmark entrypoint. + +Required DBPM provision output: + +- public tenant / host profiles usable by the load runner; +- business API/table metadata usable by public host routing; +- business operation profiles for the public workload; +- enough provisioned public route/profile count to satisfy `k` unless the config explicitly allows under-provisioning; +- a machine-readable provision/preflight report. + +### 1.4 Preflight hard gates + +Public preflight hard gates: + +- DBPM provision completed without unexpected errors; +- provisioned tenant/host/profile counts satisfy the selected `k`, unless under-provisioning is explicitly allowed; +- provisioned benchmark-owned business table count satisfies the selected `k`, unless under-provisioning is explicitly allowed; +- generated request profiles are non-empty; +- generated operation profiles are non-empty; +- public access preparation did not touch non-benchmark objects; +- route probes completed successfully; +- route probes succeed for every probed generated profile; +- GraphQL responses contain zero unexpected errors; +- the preflight artifact was written. + +If any hard gate fails, the matrix case fails before measured load starts. + +### 1.5 Preflight output shape + +Preflight returns a structured result to the matrix runner. + +Minimum shape: + +```ts +interface PublicPreflightResult { + ok: boolean; + routingMode: 'public'; + scaleProfile: string; + workloadProfile: string; + k: number; + provisionedTenantCount: number; + publicHostCount: number; + requestProfileCount: number; + operationProfileCount: number; + routeProbe: RouteProbeSummary; + requestProfiles: RequestProfile[]; + operationProfiles: OperationProfile[]; + artifactPath: string; + hardGateFailures: string[]; + errorSamples: RedactedErrorSample[]; +} +``` + +## 2. Matrix benchmark execution spec + +The matrix benchmark is the public benchmark entrypoint. + +### 2.1 Runner responsibilities + +The runner should: + +1. enforce local/opt-in guards; +2. load the selected config group/spec; +3. expand matrix cases from routing modes, cache modes, scale profiles, and workload profiles; +4. create a run directory; +5. initialize a benchmark context manager; +6. run each case in a deterministic order; +7. write per-case reports; +8. write an aggregate summary; +9. tear down all managed contexts. + +### 2.2 Connection lifecycle policy + +Connection lifecycle is configurable. + +Default policy: + +```ts +type ConnectionPolicy = 'reuse' | 'per-case'; +``` + +- Default: `reuse`. +- `reuse` means the runner should not blindly call `getConnections()` for every matrix case. +- The context manager should reuse an existing benchmark context when the target database/server state is compatible with the next case. +- If a server-level setting cannot be changed safely inside an existing context, the context manager may create a new compatible context key rather than forcing unsafe mutation. +- `per-case` means every matrix case gets a fresh `getConnections()` context and teardown. + +The report must record which connection policy was used and which context id each case ran against. + +### 2.3 Context compatibility + +Context compatibility should account for settings that affect the database/server lifecycle, including: + +- routing mode; +- cache mode; +- exposed schemas; +- anonymous/authenticated role settings; +- DBPM provision state; +- workload state when public mutations are involved. + +A reused context must not let one case's mutating workload invalidate another case's comparison. If public business data is reused across cases, the runner must either: + +- reset benchmark-owned data before the next measured case; or +- provision case-scoped benchmark-owned data so cases do not collide. + +### 2.4 Case execution flow + +Each matrix case should follow this flow: + +1. **Acquire context** + - Ask the context manager for a compatible benchmark context according to the connection policy. + +2. **Prepare profiles** + - For `public`, run public preflight. + - For `private`, build private request profiles and run lightweight setup/probe logic. + +3. **Prepare isolation** + - If the context is reused and the workload mutates data, reset or isolate benchmark-owned data before measured load. + +4. **Capture memory/cache before** + - Capture `/debug/memory` or equivalent debug snapshot when available. + +5. **Run final route probe** + - Confirm the selected request profiles still work immediately before measured load. + +6. **Run measured load** + - Run for the selected duration and worker count. + - Distribute requests across profiles. + - Select operations according to the workload profile. + - Apply fail-fast rules. + +7. **Capture memory/cache after** + - Capture the same snapshot shape as the before snapshot. + +8. **Evaluate gates** + - Convert preflight, route probe, load, memory/report, and reset failures into hard gate failures. + +9. **Write case report** + - Include metrics, artifacts, context id, lifecycle policy, and hard gate failures. + +10. **Post-case cleanup** + - If reusing the context, leave it in a known state for the next compatible case. + - If using `per-case`, teardown immediately. + +### 2.5 Failure behavior + +- Preflight failure fails the case and skips measured load. +- Route probe failure fails the case and skips measured load. +- Fail-fast during load fails the case and records the reason. +- A failed case should still write its case report when possible. +- The aggregate summary should include both passed and failed cases. + +### 2.6 Memory and cache snapshots + +Each measured case should capture memory/cache snapshots before and after load when the debug endpoint is available. + +Snapshots should record useful fields such as: + +- process memory / heap fields exposed by the server; +- multi-tenancy cache size/counters when present; +- Graphile build counters when present. + +Because the server is started through `getConnections()` inside the Jest process, process-level heap measurements are relative local signals, not isolated production memory measurements. Cache counters and before/after deltas are still useful observations. + +### 2.7 Load runner + +The load runner should be duration- and worker-based. + +Required behavior: + +- run requests for the selected `durationSeconds`; +- use the selected concurrent worker count; +- distribute requests across selected request profiles; +- select operations according to the workload profile; +- collect latency, request count, error count, and representative error samples; +- support early stop when fail-fast conditions are met. + +### 2.8 Fail-fast + +Long benchmark cases should not keep running when setup is obviously wrong. + +Initial fail-fast behavior should cover: + +- repeated network failures; +- repeated HTTP failures; +- repeated unexpected GraphQL errors; +- error rates high enough that continuing would not produce useful benchmark data. + +Fail-fast should mark the case as failed and include the reason in the case report. + +### 2.9 Case report shape + +Each case should produce a machine-readable report containing at least: + +- routing mode; +- cache mode; +- scale profile; +- workload profile; +- connection policy; +- context id; +- started/finished timestamps; +- total requests; +- failed request count; +- unexpected GraphQL error count; +- QPS / requests per second; +- p50 / p95 / p99 latency; +- representative error samples; +- preflight artifact path for public cases; +- memory/cache snapshot paths; +- hard gate failures; +- final pass/fail status. + +The run should also write an aggregate summary for all cases in the selected config group. + +## 3. Directory and file layout spec + +Initial target layout: + +```text +graphql/server-test/perf/ + README.md + SPEC.md + jest.config.js + e2e-matrix.perf.ts + config-groups/ + smoke.json + public-smoke.json + private-cache-compare.json + private-cache-distinct.json + k10-5min.json + reports/ + .gitignore + src/ + artifacts.ts + ci-guard.ts + config.ts + context.ts + dbpm-provision.ts + gates.ts + load.ts + matrix.ts + setup.ts + memory.ts + operations.ts + preflight.ts + profiles.ts + reset.ts + stats.ts + types.ts +``` + +### 3.1 Entrypoint + +`e2e-matrix.perf.ts` + +- Jest benchmark entrypoint. +- Loads config group/spec. +- Expands matrix cases. +- Calls the matrix runner. +- Contains minimal orchestration only; implementation details live under `src/`. + +### 3.2 Config and guards + +`src/config.ts` + +- Defines built-in config groups. +- Loads spec JSON files. +- Applies environment overrides according to the parameter interface in this spec. +- Produces a normalized `PerfRunConfig`. + +`src/ci-guard.ts` + +- Enforces local/opt-in execution. +- Refuses default CI execution unless a future explicit opt-in is configured. + +### 3.3 Matrix and context lifecycle + +`src/matrix.ts` + +- Expands config into cases. +- Owns deterministic run order. +- Calls case execution and aggregate reporting. + +`src/context.ts` + +- Owns `getConnections()` lifecycle. +- Implements connection policy: `reuse` by default, `per-case` when configured. +- Tracks context ids, compatibility keys, and teardown. + +### 3.4 Public preflight and DBPM provision + +`src/preflight.ts` + +- Implements public-only preflight orchestration. +- Calls DBPM provision, access preparation, profile generation, scale validation, route probes, and artifact writing. + +`src/dbpm-provision.ts` + +- Implements callable DBPM provision helper used by public preflight. +- Does not expose a standalone benchmark command. + +### 3.5 Profiles, operations, and load + +`src/setup.ts` + +- Owns GraphQL-first setup after the server starts. +- Keeps direct SQL/bootstrap work limited to the minimum needed before GraphQL can serve requests. +- Provides helpers for benchmark-owned setup operations used by preflight and private case setup. + +`src/profiles.ts` + +- Defines and builds request profiles. +- Converts provision/preflight output into load-runner inputs. + +`src/operations.ts` + +- Defines workload operations and operation weights. +- Keeps private metadata/read and public business CRUD workloads explicit. + +`src/load.ts` + +- Implements duration/worker load loop. +- Records request outcomes and error samples. +- Applies fail-fast signals from the runner. + +### 3.6 Measurements, gates, and reports + +`src/memory.ts` + +- Captures memory/cache snapshots. +- Normalizes debug endpoint responses. + +`src/stats.ts` + +- Computes request totals, QPS, p50, p95, p99, and error summaries. + +`src/gates.ts` + +- Converts preflight/probe/load/report failures into hard gate failures. +- Keeps QPS/latency as observations unless a later spec explicitly adds thresholds. + +`src/artifacts.ts` + +- Creates run directories. +- Writes JSON artifacts atomically where practical. +- Owns artifact path conventions. + +`src/reset.ts` + +- Resets or isolates benchmark-owned data when a reused context would otherwise leak state across cases. +- Must not touch non-benchmark objects by default. + +`src/types.ts` + +- Defines shared types for configs, matrix cases, contexts, profiles, preflight results, load results, reports, gates, and artifacts. + +## 4. Fixture and setup strategy + +The setup strategy is GraphQL-first. + +Core principle: + +- After the benchmark server has started, benchmark setup and benchmark operations should use GraphQL whenever the product surface can express the operation. +- SQL/bootstrap fixtures should be rare and limited to what must exist before the GraphQL server can boot or before GraphQL can expose the required API surface. + +### 4.1 Allowed direct SQL/bootstrap use + +Direct SQL or seed adapters are allowed only for bootstrap boundaries such as: + +- creating roles, schemas, extensions, or baseline database objects required for the server to start; +- installing minimal module/database structure that must exist before DBPM GraphQL operations are available; +- creating benchmark-owned guard schemas/tables that cannot be created through GraphQL yet; +- teardown/reset of benchmark-owned objects when the context manager reuses a context and GraphQL cannot safely reset the data; +- test-only assertions against database state, when those assertions do not mutate non-benchmark objects. + +Direct SQL/bootstrap must not become the normal way to provision tenants, APIs, hosts, or business workload data after the server is running. + +### 4.2 GraphQL-first setup after server start + +Once `getConnections()` has started the server, setup should proceed through GraphQL-facing behavior: + +- DBPM provision should be driven through GraphQL operations against the active benchmark server; +- public tenant/API/host setup should use GraphQL mutations where available; +- business API/table metadata should be created through the GraphQL surface where available; +- business workload seed data should be created through GraphQL mutations where available; +- route probes and workload validation should use HTTP GraphQL requests, not direct database reads, for pass/fail behavior. + +Direct database reads may still be used for diagnostics or artifact enrichment, but hard gates should prefer observable GraphQL behavior when possible. + +### 4.3 Benchmark-owned data rule + +All setup, GraphQL or SQL, must stay inside benchmark-owned scope. + +Benchmark-owned scope means: + +- generated tenant/API/host names include a run/case prefix or another unambiguous benchmark marker; +- generated schemas/tables are clearly benchmark-owned; +- generated data can be reset or discarded without affecting non-benchmark state; +- reports include enough identifiers to audit what was created. + +### 4.4 Reused context cleanup + +Because the default connection policy is `reuse`, context reuse must not leak mutating workload state into later cases. + +Preferred cleanup order: + +1. avoid collision by creating case-scoped benchmark-owned GraphQL data; +2. reset benchmark-owned data through GraphQL operations when product APIs support it; +3. use direct SQL reset only for benchmark-owned objects when GraphQL reset is not available or not reliable enough for the benchmark. + +## 5. Parameter interface and override precedence + +The first implementation should use environment variables plus optional JSON config overlays. This keeps Jest invocation simple and avoids relying on custom Jest CLI flags. + +### 5.1 Required opt-in + +A benchmark run requires: + +```sh +PERF_BENCHMARK=1 +``` + +If `PERF_BENCHMARK` is not set to `1`, the Jest perf entrypoint must refuse to run. + +CI guard: + +```sh +ALLOW_PERF_IN_CI=1 +``` + +is required only if a future CI perf job intentionally runs these benchmarks under `CI=true`. + +### 5.2 Primary selectors + +| Env var | Default | Meaning | +| --- | --- | --- | +| `PERF_CONFIG_GROUP` | `smoke` | Built-in config group to run: `smoke`, `public-smoke`, `private-cache-compare`, `private-cache-distinct`, or `k10-5min` | +| `PERF_CONFIG_PATH` | unset | Optional JSON config overlay path. When provided, it overlays the selected config group. | +| `PERF_SPEC` | unset | Legacy alias for `PERF_CONFIG_PATH`; do not use for new commands. | +| `PERF_RUN_DIR` | generated under `/tmp/constructive-perf/` | Artifact output directory | +| `PERF_CONNECTION_POLICY` | `reuse` | `reuse` or `per-case` | +| `PERF_CONSTRUCTIVE_LOCAL_PATH` | auto-discovered | Explicit path to `constructive-db/services/constructive-local` | + +### 5.3 Matrix overrides + +| Env var | Meaning | +| --- | --- | +| `PERF_ROUTING_MODES` | Comma-separated override for routing modes, e.g. `private,public` | +| `PERF_CACHE_MODES` | Comma-separated override for cache modes, e.g. `old,new` | +| `PERF_K` | Override selected scale profile `k` | +| `PERF_DURATION_SECONDS` | Override selected scale profile duration | +| `PERF_WORKERS` | Override selected scale profile worker count | +| `PERF_PRIVATE_WORKLOAD` | Override private workload profile name | +| `PERF_PUBLIC_WORKLOAD` | Override public workload profile name | +| `PERF_ALLOW_UNDERPROVISIONED` | If `1`, allow explicit under-provisioning for smoke/debug runs | +| `PERF_GRAPHILE_CACHE_MAX` | Set and record `GRAPHILE_CACHE_MAX` for the benchmark server process | +| `PERF_MULTI_TENANCY_CACHE_MAX` | Set and record `GRAPHILE_MULTI_TENANCY_CACHE_MAX` for the benchmark server process | +| `PERF_PG_CACHE_MAX` | Set and record `PG_CACHE_MAX` for the benchmark server process | + +### 5.4 Runtime behavior overrides + +| Env var | Default | Meaning | +| --- | --- | --- | +| `PERF_FAIL_FAST` | `1` | Enable fail-fast during measured load | +| `PERF_CAPTURE_MEMORY` | `1` | Capture memory/cache snapshots when debug endpoint is available | +| `PERF_ROUTE_PROBE_SAMPLE_SIZE` | group-defined | Number of request profiles to probe before load; `0` means all profiles | +| `PERF_ERROR_SAMPLE_LIMIT` | `20` | Maximum representative error samples stored in reports | +| `PERF_TEST_TIMEOUT_MS` | group-derived | Jest timeout for the perf entrypoint | + +### 5.5 Override precedence + +Config is resolved in this order: + +```text +built-in defaults + < built-in config group + < PERF_CONFIG_PATH JSON overlay + < PERF_* environment overrides +``` + +The normalized config written to `summary.json` must include both the resolved values and enough source metadata to explain which group/config overlay/env overrides produced the run. + +### 5.6 JSON config overlay shape + +A JSON config overlay may override any normalized config field, but should keep the same concepts as the built-in groups. + +Minimal shape: + +```json +{ + "name": "k10-5min", + "connectionPolicy": "reuse", + "routingModes": ["private", "public"], + "cacheModes": ["old", "new"], + "scaleProfile": { + "name": "k10-5min", + "k": 10, + "durationSeconds": 300, + "workers": 4 + }, + "workloadProfiles": { + "private": "cache-key-shared", + "public": "business-crud" + }, + "cacheSizes": { + "graphileCacheMax": 50, + "multiTenancyCacheMax": 50, + "pgCacheMax": 50 + }, + "publicPreflight": { + "allowUnderProvisioned": false + } +} +``` + +## 6. Report and artifact schema + +Reports are machine-readable JSON artifacts. They are part of the target behavior, not incidental debug output. + +### 6.1 Common rules + +- Every JSON artifact should include `schemaVersion`. +- Timestamps should be ISO strings. +- Reports must redact secrets, tokens, cookies, passwords, and authorization headers. +- Error samples should be representative and capped by `PERF_ERROR_SAMPLE_LIMIT`. +- Paths should be absolute or relative to `runDir`, but the convention must be consistent within a run. + +### 6.2 Run summary: `summary.json` + +Minimum shape: + +```ts +interface PerfRunSummary { + schemaVersion: 1; + runId: string; + runDir: string; + configGroup: string; + configPath?: string; + startedAt: string; + finishedAt: string; + pass: boolean; + config: NormalizedPerfRunConfig; + totals: { + caseCount: number; + passed: number; + failed: number; + skipped: number; + }; + cases: CaseSummary[]; + artifacts: { + summaryPath: string; + casesDir: string; + preflightDir: string; + memoryDir: string; + errorsDir: string; + }; +} +``` + +### 6.3 Case report: `cases/.json` + +Minimum shape: + +```ts +interface CaseReport { + schemaVersion: 1; + runId: string; + caseId: string; + startedAt: string; + finishedAt: string; + ok: boolean; + matrix: { + routingMode: 'private' | 'public'; + cacheMode: 'old' | 'new'; + scaleProfile: ScaleProfile; + workloadProfile: string; + }; + lifecycle: { + connectionPolicy: 'reuse' | 'per-case'; + contextId: string; + contextReused: boolean; + compatibilityKey: string; + serverUrl: string; + }; + preflight?: { + ok: boolean; + artifactPath: string; + provisionedTenantCount?: number; + publicHostCount?: number; + requestProfileCount: number; + operationProfileCount: number; + hardGateFailures: string[]; + }; + routeProbe: RouteProbeSummary; + load?: LoadReport; + memory?: { + beforePath?: string; + afterPath?: string; + beforeOk: boolean; + afterOk: boolean; + }; + gates: { + hardGateFailures: string[]; + observations: Record; + }; + artifacts: { + caseReportPath: string; + preflightPath?: string; + memoryBeforePath?: string; + memoryAfterPath?: string; + errorsPath?: string; + }; +} +``` + +### 6.4 Preflight report: `preflight/.json` + +Minimum shape: + +```ts +interface PreflightReport { + schemaVersion: 1; + runId: string; + caseId: string; + startedAt: string; + finishedAt: string; + ok: boolean; + configSnapshot: Record; + provision: { + ok: boolean; + tenantCount: number; + publicHostCount: number; + apiCount: number; + businessTableCount: number; + reportPath?: string; + errors: RedactedErrorSample[]; + source: 'graphql' | 'unavailable'; + warnings: string[]; + }; + profiles: { + requestProfileCount: number; + operationProfileCount: number; + routeKeyCount: number; + }; + routeProbe: RouteProbeSummary; + hardGateFailures: string[]; +} +``` + +### 6.5 Request profile metadata + +Public request profiles generated from DBPM preflight must preserve enough metadata to audit and operate against the generated public target. + +Minimum public profile metadata: + +```ts +interface PublicRequestProfileMetadata { + source: 'constructive-local-dbpm-graphql'; + host: string; + apiId: string; + domainId: string; + databaseId: string; + schemaId: string; + schemaName: string; + tableName: string; + tableId: string; + listField: string; + createField?: string; + updateField?: string; +} +``` + +The public workload must use the generated metadata rather than hard-coded public field names when the table/field names are run- or case-scoped. + +### 6.6 Load report + +Minimum shape embedded in the case report: + +```ts +interface LoadReport { + ok: boolean; + durationSeconds: number; + workers: number; + totalRequests: number; + failedRequests: number; + unexpectedGraphqlErrors: number; + qps: number; + latencyMs: { + p50: number | null; + p90: number | null; + p95: number | null; + p99: number | null; + max: number | null; + }; + operations: Record; + failFast?: { + triggered: boolean; + reason?: string; + }; + errorSamples: RedactedErrorSample[]; +} +``` + +### 6.7 Load/statistics robustness + +The load runner and statistics code must handle long local runs and high request counts. + +Required robustness behavior: + +- do not use JavaScript argument spreading such as `Math.max(...values)` or `Math.min(...values)` on latency arrays that can grow with request count; +- compute latency min/max/percentiles in a way that does not hit call stack or argument-count limits; +- if load or statistics throws, the case should still write a case report and error artifact when possible; +- thrown failures must be represented as hard gate failures. + +### 6.8 Hard gates vs observations + +Hard gates fail a case. Initial hard gates are: + +- local/opt-in guard passed; +- context acquisition succeeded; +- public preflight passed for public cases; +- route probes passed; +- measured load had zero unexpected GraphQL errors unless the case explicitly expects errors; +- measured load had zero unexpected HTTP/network failures; +- memory/cache snapshots were readable when capture is enabled; +- reports were written successfully; +- reused contexts did not leak mutating benchmark data across cases. + +Observations are recorded but do not fail by default: + +- QPS; +- latency percentiles; +- heap delta; +- cache size/counters; +- Graphile build counters. + +Threshold-based performance failures require an explicit future spec setting. + +## 7. Target implementation completeness + +This README and spec describe the target implementation. They are not a phased migration plan. + +The implementation is considered complete when: + +- the target directory/file layout exists; +- the Jest perf entrypoint runs config groups through the matrix runner; +- `smoke`, `public-smoke`, `private-cache-compare`, `private-cache-distinct`, and `k10-5min` are represented as built-in config groups or specs; +- `getConnections()` installs `constructive-local` for the benchmark baseline; +- public cases run DBPM provision through public preflight using the private GraphQL DBPM surface on the same test database; +- `public-smoke` can provision enough public profiles for its selected `k` and complete measured load through public host routing; +- public/new `k10-5min` can provision `k=10` public profiles and complete the selected measured load duration through public host routing; +- setup after server start is GraphQL-first; +- connection policy defaults to `reuse` and supports `per-case`; +- per-case and aggregate JSON reports are written; +- hard gates and observations follow this spec; +- default unit/integration test runs do not pick up perf benchmarks; +- CI execution is refused unless explicitly opted in. + +## 8. Artifacts + +A benchmark run should write artifacts under an explicit run directory. + +Suggested shape: + +```text +/ + summary.json + cases/ + .json + preflight/ + .json + memory/ + -before.json + -after.json + errors/ + .json +``` + +Generated artifacts should stay out of git. + +## First-version non-goals + +- No default CI execution. +- No external benchmark CLI/script execution. +- No top-level preflight command. +- No strict QPS/latency regression threshold by default. +- No sweep/stress command surface in the first version. +- No shape-variant dimension unless explicitly added later. +- No developer-specific absolute path to `constructive-local`. +- No requirement that the first public workload exercise full create/update/delete mutations before those mutation shapes are explicitly stabilized for the benchmark. diff --git a/graphql/server-test/perf/__tests__/suite.perf.ts b/graphql/server-test/perf/__tests__/suite.perf.ts new file mode 100644 index 0000000000..55a1d25c5f --- /dev/null +++ b/graphql/server-test/perf/__tests__/suite.perf.ts @@ -0,0 +1,215 @@ +import fs from 'fs'; +import os from 'os'; +import path from 'path'; + +import { redact, toRedactedErrorSample, writeJsonArtifact } from '../src/artifacts'; +import { enforcePerfOptIn } from '../src/ci-guard'; +import { discoverConstructiveLocalPath, loadPerfConfig } from '../src/config'; +import { expandMatrix } from '../src/matrix'; +import { buildPublicOperationProfiles } from '../src/operations'; +import { buildPrivateProfiles } from '../src/profiles'; +import { summarizeOutcomes } from '../src/stats'; +import type { RequestOutcome, RequestProfile } from '../src/types'; + +const makeConstructiveLocal = (root: string): string => { + const service = path.join(root, 'constructive-db', 'services', 'constructive-local'); + fs.mkdirSync(service, { recursive: true }); + fs.writeFileSync(path.join(service, 'pgpm.plan'), '%syntax-version=1.0.0\n%project=constructive-local\n'); + fs.writeFileSync(path.join(service, 'constructive-local.control'), 'default_version = 0.0.1\n'); + return service; +}; + +describe('perf suite harness', () => { + it('requires explicit local opt-in', () => { + expect(() => enforcePerfOptIn({})).toThrow(/PERF_BENCHMARK=1/); + expect(() => enforcePerfOptIn({ PERF_BENCHMARK: '1' })).not.toThrow(); + }); + + it('requires an additional CI opt-in', () => { + expect(() => enforcePerfOptIn({ PERF_BENCHMARK: '1', CI: 'true' })).toThrow(/ALLOW_PERF_IN_CI=1/); + expect(() => + enforcePerfOptIn({ PERF_BENCHMARK: '1', CI: 'true', ALLOW_PERF_IN_CI: '1' }) + ).not.toThrow(); + }); + + it('discovers constructive-local through override and sibling layout', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'perf-path-')); + const service = makeConstructiveLocal(root); + expect(discoverConstructiveLocalPath({ env: { PERF_CONSTRUCTIVE_LOCAL_PATH: service } })).toBe(service); + + const constructive = path.join(root, 'constructive'); + fs.mkdirSync(path.join(constructive, 'graphql', 'server-test'), { recursive: true }); + expect(discoverConstructiveLocalPath({ cwd: path.join(constructive, 'graphql', 'server-test'), env: {} })).toBe(service); + }); + + it('loads config groups with deterministic env overrides', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'perf-config-')); + const service = makeConstructiveLocal(root); + const configPath = path.join(root, 'custom-config.json'); + fs.writeFileSync(configPath, JSON.stringify({ name: 'custom-k2' })); + const config = loadPerfConfig({ + PERF_CONSTRUCTIVE_LOCAL_PATH: service, + PERF_CONFIG_GROUP: 'k10-5min', + PERF_CONFIG_PATH: configPath, + PERF_ROUTING_MODES: 'public,private', + PERF_CACHE_MODES: 'new', + PERF_K: '2', + PERF_DURATION_SECONDS: '1', + PERF_WORKERS: '1', + PERF_CAPTURE_MEMORY: '0', + PERF_GRAPHILE_CACHE_MAX: '25', + PERF_MULTI_TENANCY_CACHE_MAX: '15', + PERF_PG_CACHE_MAX: '25', + }); + + expect(config.routingModes).toEqual(['public', 'private']); + expect(config.cacheModes).toEqual(['new']); + expect(config.scaleProfile).toMatchObject({ k: 2, durationSeconds: 1, workers: 1 }); + expect(config.captureMemory).toBe(false); + expect(config.constructiveLocalPath).toBe(service); + expect(config.configPath).toBe(configPath); + expect(config.name).toBe('custom-k2'); + expect(config.cacheSizes).toEqual({ graphileCacheMax: 25, multiTenancyCacheMax: 15, pgCacheMax: 25 }); + expect(config.effectiveCacheEnv).toEqual({ + GRAPHILE_CACHE_MAX: '25', + GRAPHILE_MULTI_TENANCY_CACHE_MAX: '15', + PG_CACHE_MAX: '25', + }); + }); + + it('expands matrix cases deterministically', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'perf-matrix-')); + const service = makeConstructiveLocal(root); + const config = loadPerfConfig({ + PERF_CONSTRUCTIVE_LOCAL_PATH: service, + PERF_CONFIG_GROUP: 'k10-5min', + PERF_DURATION_SECONDS: '1', + }); + + expect(expandMatrix(config).map((item) => item.caseId)).toEqual([ + 'private-old-k10-5min-cache-key-shared', + 'private-new-k10-5min-cache-key-shared', + 'public-old-k10-5min-business-crud', + 'public-new-k10-5min-business-crud', + ]); + }); + + it('builds private shared and distinct cache-key pressure profiles', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'perf-private-profiles-')); + const service = makeConstructiveLocal(root); + const sharedConfig = loadPerfConfig({ + PERF_CONSTRUCTIVE_LOCAL_PATH: service, + PERF_CONFIG_GROUP: 'private-cache-compare', + PERF_K: '4', + PERF_DURATION_SECONDS: '1', + }); + const sharedCase = expandMatrix(sharedConfig)[0]; + const shared = buildPrivateProfiles(sharedCase, sharedConfig); + + expect(shared.requestProfiles).toHaveLength(4); + expect(new Set(shared.requestProfiles.map((profile) => profile.routeKey)).size).toBe(4); + expect(new Set(shared.requestProfiles.map((profile) => profile.metadata.expectedBuildKeyGroup)).size).toBe(1); + + const distinctConfig = loadPerfConfig({ + PERF_CONSTRUCTIVE_LOCAL_PATH: service, + PERF_CONFIG_GROUP: 'private-cache-distinct', + PERF_K: '4', + PERF_DURATION_SECONDS: '1', + }); + const distinctCase = expandMatrix(distinctConfig)[0]; + const distinct = buildPrivateProfiles(distinctCase, distinctConfig); + + expect(distinct.requestProfiles).toHaveLength(4); + expect(new Set(distinct.requestProfiles.map((profile) => profile.routeKey)).size).toBe(4); + expect(new Set(distinct.requestProfiles.map((profile) => profile.metadata.expectedBuildKeyGroup)).size).toBe(4); + }); + + it('builds public CRUD operation profiles from DBPM metadata', () => { + const operations = buildPublicOperationProfiles('business-crud'); + expect(operations.map((operation) => operation.name)).toEqual([ + 'public.business.create', + 'public.business.getById', + 'public.business.updateById', + 'public.business.listRecent', + 'public.typename', + ]); + + const requestProfile: RequestProfile = { + id: 'public-dbpm-1', + routingMode: 'public', + routeKey: 'perf.localhost', + headers: { Host: 'perf.localhost' }, + description: 'test profile', + benchmarkOwned: true, + metadata: { + listField: 'perfItems', + createField: 'createPerfItem', + updateField: 'updatePerfItem', + createInputType: 'CreatePerfItemInput', + updateInputType: 'UpdatePerfItemInput', + nodeField: 'perfItem', + patchField: 'perfItemPatch', + }, + }; + const create = operations.find((operation) => operation.name === 'public.business.create')!; + const update = operations.find((operation) => operation.name === 'public.business.updateById')!; + + expect(create.buildQuery?.({ sequence: 1, requestProfile, config: {} as any, matrixCase: {} as any })) + .toContain('mutation PerfPublicBusinessCreate($input: CreatePerfItemInput!)'); + expect(create.buildVariables?.({ sequence: 1, requestProfile, config: { benchmarkOwnedPrefix: 'perf' } as any, matrixCase: {} as any })) + .toEqual({ input: { perfItem: { label: 'perf-public-dbpm-1-1' } } }); + expect(update.requiresExistingRow).toBe(true); + expect(update.buildVariables?.({ + sequence: 2, + rowId: 'row-1', + requestProfile, + config: { benchmarkOwnedPrefix: 'perf' } as any, + matrixCase: {} as any, + })).toEqual({ input: { id: 'row-1', perfItemPatch: { label: 'perf-public-dbpm-1-2' } } }); + }); + + it('redacts secrets in nested reports and error samples', async () => { + const value = redact({ + headers: { + Authorization: 'Bearer abc.def', + cookie: 'session=secret', + }, + url: 'https://example.test?token=secret&ok=1', + }); + + expect(JSON.stringify(value)).not.toContain('abc.def'); + expect(JSON.stringify(value)).not.toContain('session=secret'); + expect(JSON.stringify(value)).not.toContain('token=secret'); + + const sample = toRedactedErrorSample({ message: 'Authorization=Bearer abc.def password=hunter2' }); + expect(JSON.stringify(sample)).not.toContain('hunter2'); + + const target = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'perf-artifact-')), 'artifact.json'); + await writeJsonArtifact(target, { schemaVersion: 1, Authorization: 'Bearer abc.def' }); + expect(fs.readFileSync(target, 'utf8')).not.toContain('abc.def'); + }); + + it('summarizes large latency arrays without spreading', () => { + const outcomes: RequestOutcome[] = Array.from({ length: 150_000 }, (_, index) => ({ + ok: true, + latencyMs: index % 200, + operation: 'op', + requestProfileId: 'profile', + status: 200, + unexpectedGraphqlErrors: 0, + })); + + const report = summarizeOutcomes({ + outcomes, + durationMs: 1000, + durationSeconds: 1, + workers: 1, + errorSampleLimit: 20, + failFast: { triggered: false }, + }); + + expect(report.totalRequests).toBe(150_000); + expect(report.latencyMs.max).toBe(199); + expect(report.failedRequests).toBe(0); + }); +}); diff --git a/graphql/server-test/perf/config-groups/k10-5min.json b/graphql/server-test/perf/config-groups/k10-5min.json new file mode 100644 index 0000000000..9a9399af52 --- /dev/null +++ b/graphql/server-test/perf/config-groups/k10-5min.json @@ -0,0 +1,19 @@ +{ + "name": "k10-5min", + "connectionPolicy": "reuse", + "routingModes": ["private", "public"], + "cacheModes": ["old", "new"], + "scaleProfile": { + "name": "k10-5min", + "k": 10, + "durationSeconds": 300, + "workers": 4 + }, + "workloadProfiles": { + "private": "cache-key-shared", + "public": "business-crud" + }, + "publicPreflight": { + "allowUnderProvisioned": false + } +} diff --git a/graphql/server-test/perf/config-groups/private-cache-compare.json b/graphql/server-test/perf/config-groups/private-cache-compare.json new file mode 100644 index 0000000000..0ff82dedcf --- /dev/null +++ b/graphql/server-test/perf/config-groups/private-cache-compare.json @@ -0,0 +1,19 @@ +{ + "name": "private-cache-compare", + "connectionPolicy": "reuse", + "routingModes": ["private"], + "cacheModes": ["old", "new"], + "scaleProfile": { + "name": "private-cache-compare", + "k": 10, + "durationSeconds": 60, + "workers": 2 + }, + "workloadProfiles": { + "private": "cache-key-shared", + "public": "business-crud" + }, + "publicPreflight": { + "allowUnderProvisioned": false + } +} diff --git a/graphql/server-test/perf/config-groups/private-cache-distinct.json b/graphql/server-test/perf/config-groups/private-cache-distinct.json new file mode 100644 index 0000000000..6051d7ea4c --- /dev/null +++ b/graphql/server-test/perf/config-groups/private-cache-distinct.json @@ -0,0 +1,19 @@ +{ + "name": "private-cache-distinct", + "connectionPolicy": "reuse", + "routingModes": ["private"], + "cacheModes": ["old", "new"], + "scaleProfile": { + "name": "private-cache-distinct", + "k": 10, + "durationSeconds": 60, + "workers": 2 + }, + "workloadProfiles": { + "private": "cache-key-distinct", + "public": "business-crud" + }, + "publicPreflight": { + "allowUnderProvisioned": false + } +} diff --git a/graphql/server-test/perf/config-groups/public-smoke.json b/graphql/server-test/perf/config-groups/public-smoke.json new file mode 100644 index 0000000000..e8fe14c626 --- /dev/null +++ b/graphql/server-test/perf/config-groups/public-smoke.json @@ -0,0 +1,19 @@ +{ + "name": "public-smoke", + "connectionPolicy": "reuse", + "routingModes": ["public"], + "cacheModes": ["new"], + "scaleProfile": { + "name": "public-smoke", + "k": 1, + "durationSeconds": 5, + "workers": 1 + }, + "workloadProfiles": { + "private": "metadata-read", + "public": "business-crud" + }, + "publicPreflight": { + "allowUnderProvisioned": false + } +} diff --git a/graphql/server-test/perf/config-groups/smoke.json b/graphql/server-test/perf/config-groups/smoke.json new file mode 100644 index 0000000000..4d33f338a9 --- /dev/null +++ b/graphql/server-test/perf/config-groups/smoke.json @@ -0,0 +1,19 @@ +{ + "name": "smoke", + "connectionPolicy": "reuse", + "routingModes": ["private"], + "cacheModes": ["new"], + "scaleProfile": { + "name": "smoke", + "k": 1, + "durationSeconds": 5, + "workers": 1 + }, + "workloadProfiles": { + "private": "metadata-read", + "public": "business-crud" + }, + "publicPreflight": { + "allowUnderProvisioned": false + } +} diff --git a/graphql/server-test/perf/e2e-matrix.perf.ts b/graphql/server-test/perf/e2e-matrix.perf.ts new file mode 100644 index 0000000000..067308be8a --- /dev/null +++ b/graphql/server-test/perf/e2e-matrix.perf.ts @@ -0,0 +1,20 @@ +import { enforcePerfOptIn } from './src/ci-guard'; +import { loadPerfConfig } from './src/config'; +import { runPerfMatrix } from './src/matrix'; + +const config = loadPerfConfig(); + +jest.setTimeout(config.testTimeoutMs); + +describe('graphql-server-test perf matrix', () => { + it(`runs ${config.configGroup}`, async () => { + enforcePerfOptIn(); + const summary = await runPerfMatrix(config); + if (!summary.pass) { + throw new Error( + `Perf matrix failed: ${summary.totals.failed} failed, ${summary.totals.skipped} skipped. ` + + `Summary: ${summary.artifacts.summaryPath}` + ); + } + }); +}); diff --git a/graphql/server-test/perf/jest.config.js b/graphql/server-test/perf/jest.config.js new file mode 100644 index 0000000000..69b7d7d3b6 --- /dev/null +++ b/graphql/server-test/perf/jest.config.js @@ -0,0 +1,21 @@ +module.exports = { + displayName: 'graphql-server-test-perf', + testEnvironment: 'node', + rootDir: '..', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + useESM: false, + }, + ], + }, + testMatch: ['/perf/**/*.perf.ts'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + modulePathIgnorePatterns: ['/dist/'], + testPathIgnorePatterns: ['/node_modules/', '/dist/', '/perf/reports/'], + collectCoverage: false, + verbose: true, + maxWorkers: 1, + forceExit: true, +}; diff --git a/graphql/server-test/perf/reports/.gitignore b/graphql/server-test/perf/reports/.gitignore new file mode 100644 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/graphql/server-test/perf/reports/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/graphql/server-test/perf/src/artifacts.ts b/graphql/server-test/perf/src/artifacts.ts new file mode 100644 index 0000000000..90f6591581 --- /dev/null +++ b/graphql/server-test/perf/src/artifacts.ts @@ -0,0 +1,107 @@ +import fs from 'fs'; +import path from 'path'; + +import { + PERF_SCHEMA_VERSION, + type RedactedErrorSample, + type RunArtifactPaths, +} from './types'; + +const SENSITIVE_KEY = /(authorization|cookie|token|secret|password|passwd|apikey|api-key|set-cookie)/i; + +export const createRunArtifactPaths = (runDir: string): RunArtifactPaths => { + const resolved = path.resolve(runDir); + return { + runDir: resolved, + summaryPath: path.join(resolved, 'summary.json'), + casesDir: path.join(resolved, 'cases'), + preflightDir: path.join(resolved, 'preflight'), + memoryDir: path.join(resolved, 'memory'), + errorsDir: path.join(resolved, 'errors'), + }; +}; + +export const ensureRunDirs = async (paths: RunArtifactPaths): Promise => { + await Promise.all([ + fs.promises.mkdir(paths.casesDir, { recursive: true }), + fs.promises.mkdir(paths.preflightDir, { recursive: true }), + fs.promises.mkdir(paths.memoryDir, { recursive: true }), + fs.promises.mkdir(paths.errorsDir, { recursive: true }), + ]); +}; + +export const caseReportPath = (paths: RunArtifactPaths, caseId: string): string => + path.join(paths.casesDir, `${caseId}.json`); + +export const preflightReportPath = (paths: RunArtifactPaths, caseId: string): string => + path.join(paths.preflightDir, `${caseId}.json`); + +export const memoryReportPath = (paths: RunArtifactPaths, caseId: string, phase: 'before' | 'after'): string => + path.join(paths.memoryDir, `${caseId}-${phase}.json`); + +export const errorsReportPath = (paths: RunArtifactPaths, caseId: string): string => + path.join(paths.errorsDir, `${caseId}.json`); + +export const redact = (value: unknown): unknown => { + if (Array.isArray(value)) return value.map((item) => redact(item)); + if (value && typeof value === 'object') { + const out: Record = {}; + for (const [key, child] of Object.entries(value as Record)) { + out[key] = SENSITIVE_KEY.test(key) ? '[REDACTED]' : redact(child); + } + return out; + } + if (typeof value === 'string') { + return value + .replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [REDACTED]') + .replace(/(authorization|token|secret|password|cookie)=([^&\s]+)/gi, '$1=[REDACTED]'); + } + return value; +}; + +export const toRedactedErrorSample = ( + error: unknown, + context: Partial = {} +): RedactedErrorSample => { + const redacted = redact(error); + let message: string; + let code: string | undefined; + let details: unknown; + + if (redacted instanceof Error) { + message = redacted.message; + details = redacted.stack; + } else if (Array.isArray(redacted)) { + const first = redacted[0] as any; + message = first?.message ? String(first.message) : JSON.stringify(redacted).slice(0, 2000); + code = first?.extensions?.code ? String(first.extensions.code) : undefined; + details = redacted; + } else if (redacted && typeof redacted === 'object') { + const obj = redacted as any; + message = obj.message ? String(obj.message) : JSON.stringify(redacted).slice(0, 2000); + code = obj.code ? String(obj.code) : obj.extensions?.code ? String(obj.extensions.code) : undefined; + details = redacted; + } else { + message = String(redacted); + } + + return { + at: new Date().toISOString(), + ...context, + message, + ...(code && { code }), + ...(details !== undefined && { details }), + }; +}; + +export const writeJsonArtifact = async (targetPath: string, value: unknown): Promise => { + await fs.promises.mkdir(path.dirname(targetPath), { recursive: true }); + const payload = redact(value); + const finalPayload = + payload && typeof payload === 'object' && !Array.isArray(payload) + ? payload + : { schemaVersion: PERF_SCHEMA_VERSION, value: payload }; + const tmpPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`; + await fs.promises.writeFile(tmpPath, `${JSON.stringify(finalPayload, null, 2)}\n`, 'utf8'); + await fs.promises.rename(tmpPath, targetPath); +}; diff --git a/graphql/server-test/perf/src/ci-guard.ts b/graphql/server-test/perf/src/ci-guard.ts new file mode 100644 index 0000000000..17e43364c7 --- /dev/null +++ b/graphql/server-test/perf/src/ci-guard.ts @@ -0,0 +1,11 @@ +export const enforcePerfOptIn = (env: NodeJS.ProcessEnv = process.env): void => { + if (env.PERF_BENCHMARK !== '1') { + throw new Error('Refusing to run perf benchmarks. Set PERF_BENCHMARK=1 to opt in.'); + } + + if (env.CI === 'true' && env.ALLOW_PERF_IN_CI !== '1') { + throw new Error( + 'Refusing to run perf benchmarks in CI. Set ALLOW_PERF_IN_CI=1 only for an explicit perf CI job.' + ); + } +}; diff --git a/graphql/server-test/perf/src/config.ts b/graphql/server-test/perf/src/config.ts new file mode 100644 index 0000000000..03aea50b03 --- /dev/null +++ b/graphql/server-test/perf/src/config.ts @@ -0,0 +1,427 @@ +import fs from 'fs'; +import path from 'path'; + +import { + PERF_SCHEMA_VERSION, + type CacheSizeConfig, + type CacheMode, + type ConnectionPolicy, + type PerfRunConfig, + type PerfRunConfigOverlay, + type RoutingMode, + type ScaleProfile, +} from './types'; + +const DEFAULT_CONFIG_GROUP = 'smoke'; +const DEFAULT_RUN_ROOT = '/tmp/constructive-perf'; +const VALID_ROUTING_MODES: RoutingMode[] = ['private', 'public']; +const VALID_CACHE_MODES: CacheMode[] = ['old', 'new']; +const VALID_CONNECTION_POLICIES: ConnectionPolicy[] = ['reuse', 'per-case']; + +const nowRunId = (): string => new Date().toISOString().replace(/[:.]/g, '-'); + +const positiveInt = (value: string | undefined, label: string): number | undefined => { + if (value == null || value === '') return undefined; + const parsed = Number.parseInt(value, 10); + if (!Number.isFinite(parsed) || parsed < 0) { + throw new Error(`${label} must be a non-negative integer, got ${value}`); + } + return parsed; +}; + +const positiveNonZeroInt = (value: string | undefined, label: string): number | undefined => { + const parsed = positiveInt(value, label); + if (parsed !== undefined && parsed <= 0) { + throw new Error(`${label} must be a positive integer, got ${value}`); + } + return parsed; +}; + +const boolEnv = (value: string | undefined): boolean | undefined => { + if (value == null || value === '') return undefined; + const normalized = value.trim().toLowerCase(); + if (['1', 'true', 'yes', 'on'].includes(normalized)) return true; + if (['0', 'false', 'no', 'off'].includes(normalized)) return false; + throw new Error(`Expected boolean env value (1/0/true/false), got ${value}`); +}; + +const parseEnumList = ( + value: string | undefined, + valid: readonly T[], + label: string +): T[] | undefined => { + if (!value) return undefined; + const items = value.split(',').map((part) => part.trim()).filter(Boolean) as T[]; + if (items.length === 0) return undefined; + for (const item of items) { + if (!valid.includes(item)) { + throw new Error(`${label} includes invalid value ${item}; expected one of ${valid.join(', ')}`); + } + } + return items; +}; + +const deepMerge = >(base: T, overlay?: Record): T => { + if (!overlay) return { ...base }; + const result: Record = { ...base }; + for (const [key, value] of Object.entries(overlay)) { + if ( + value && + typeof value === 'object' && + !Array.isArray(value) && + result[key] && + typeof result[key] === 'object' && + !Array.isArray(result[key]) + ) { + result[key] = deepMerge(result[key], value); + } else if (value !== undefined) { + result[key] = value; + } + } + return result as T; +}; + +const defaults: PerfRunConfigOverlay = { + name: DEFAULT_CONFIG_GROUP, + configGroup: DEFAULT_CONFIG_GROUP, + runDir: '', + connectionPolicy: 'reuse', + routingModes: ['private'], + cacheModes: ['new'], + scaleProfile: { + name: 'smoke', + k: 1, + durationSeconds: 5, + workers: 1, + }, + workloadProfiles: { + private: 'metadata-read', + public: 'business-crud', + }, + publicPreflight: { + allowUnderProvisioned: false, + }, + cacheSizes: {}, + failFast: true, + captureMemory: true, + routeProbeSampleSize: 1, + errorSampleLimit: 20, + testTimeoutMs: 120_000, +}; + +export const BUILT_IN_CONFIG_GROUPS: Record = { + smoke: { + name: 'smoke', + routingModes: ['private'], + cacheModes: ['new'], + scaleProfile: { name: 'smoke', k: 1, durationSeconds: 5, workers: 1 }, + routeProbeSampleSize: 1, + testTimeoutMs: 120_000, + }, + 'public-smoke': { + name: 'public-smoke', + routingModes: ['public'], + cacheModes: ['new'], + scaleProfile: { name: 'public-smoke', k: 1, durationSeconds: 5, workers: 1 }, + routeProbeSampleSize: 1, + testTimeoutMs: 180_000, + publicPreflight: { allowUnderProvisioned: false }, + }, + 'private-cache-compare': { + name: 'private-cache-compare', + routingModes: ['private'], + cacheModes: ['old', 'new'], + scaleProfile: { name: 'private-cache-compare', k: 10, durationSeconds: 60, workers: 2 }, + workloadProfiles: { private: 'cache-key-shared' }, + routeProbeSampleSize: 0, + testTimeoutMs: 300_000, + }, + 'private-cache-distinct': { + name: 'private-cache-distinct', + routingModes: ['private'], + cacheModes: ['old', 'new'], + scaleProfile: { name: 'private-cache-distinct', k: 10, durationSeconds: 60, workers: 2 }, + workloadProfiles: { private: 'cache-key-distinct' }, + routeProbeSampleSize: 0, + testTimeoutMs: 300_000, + }, + 'k10-5min': { + name: 'k10-5min', + routingModes: ['private', 'public'], + cacheModes: ['old', 'new'], + scaleProfile: { name: 'k10-5min', k: 10, durationSeconds: 300, workers: 4 }, + workloadProfiles: { private: 'cache-key-shared', public: 'business-crud' }, + routeProbeSampleSize: 0, + testTimeoutMs: 900_000, + publicPreflight: { allowUnderProvisioned: false }, + }, +}; + +const resolveConfigPath = (configPath: string): string => + path.isAbsolute(configPath) ? configPath : path.resolve(process.cwd(), configPath); + +const readConfigOverlay = (configPath?: string): PerfRunConfigOverlay | undefined => { + if (!configPath) return undefined; + const resolved = resolveConfigPath(configPath); + const parsed = JSON.parse(fs.readFileSync(resolved, 'utf8')) as PerfRunConfigOverlay; + return { ...parsed, configPath: resolved }; +}; + +const resolveEnvConfigPath = (env: NodeJS.ProcessEnv): string | undefined => { + if (env.PERF_CONFIG_PATH && env.PERF_SPEC && env.PERF_CONFIG_PATH !== env.PERF_SPEC) { + throw new Error('PERF_CONFIG_PATH and legacy PERF_SPEC both set different paths; use PERF_CONFIG_PATH only.'); + } + const configPath = env.PERF_CONFIG_PATH || env.PERF_SPEC; + return configPath ? resolveConfigPath(configPath) : undefined; +}; + +const envOverrides = (env: NodeJS.ProcessEnv): PerfRunConfigOverlay => { + const overlay: PerfRunConfigOverlay = {}; + + if (env.PERF_RUN_DIR) overlay.runDir = env.PERF_RUN_DIR; + if (env.PERF_CONNECTION_POLICY) { + if (!VALID_CONNECTION_POLICIES.includes(env.PERF_CONNECTION_POLICY as ConnectionPolicy)) { + throw new Error(`PERF_CONNECTION_POLICY must be reuse or per-case, got ${env.PERF_CONNECTION_POLICY}`); + } + overlay.connectionPolicy = env.PERF_CONNECTION_POLICY as ConnectionPolicy; + } + + const routingModes = parseEnumList(env.PERF_ROUTING_MODES, VALID_ROUTING_MODES, 'PERF_ROUTING_MODES'); + if (routingModes) overlay.routingModes = routingModes; + + const cacheModes = parseEnumList(env.PERF_CACHE_MODES, VALID_CACHE_MODES, 'PERF_CACHE_MODES'); + if (cacheModes) overlay.cacheModes = cacheModes; + + const scaleProfile: Partial = {}; + const k = positiveInt(env.PERF_K, 'PERF_K'); + if (k !== undefined) scaleProfile.k = k; + const durationSeconds = positiveInt(env.PERF_DURATION_SECONDS, 'PERF_DURATION_SECONDS'); + if (durationSeconds !== undefined) scaleProfile.durationSeconds = durationSeconds; + const workers = positiveInt(env.PERF_WORKERS, 'PERF_WORKERS'); + if (workers !== undefined) scaleProfile.workers = workers; + if (Object.keys(scaleProfile).length > 0) overlay.scaleProfile = scaleProfile; + + if (env.PERF_PRIVATE_WORKLOAD || env.PERF_PUBLIC_WORKLOAD) { + overlay.workloadProfiles = { + ...(env.PERF_PRIVATE_WORKLOAD && { private: env.PERF_PRIVATE_WORKLOAD }), + ...(env.PERF_PUBLIC_WORKLOAD && { public: env.PERF_PUBLIC_WORKLOAD }), + }; + } + + const allowUnderProvisioned = boolEnv(env.PERF_ALLOW_UNDERPROVISIONED); + if (allowUnderProvisioned !== undefined) { + overlay.publicPreflight = { allowUnderProvisioned }; + } + + const cacheSizes: CacheSizeConfig = {}; + const graphileCacheMax = positiveNonZeroInt(env.PERF_GRAPHILE_CACHE_MAX, 'PERF_GRAPHILE_CACHE_MAX'); + if (graphileCacheMax !== undefined) cacheSizes.graphileCacheMax = graphileCacheMax; + const multiTenancyCacheMax = + positiveNonZeroInt(env.PERF_MULTI_TENANCY_CACHE_MAX, 'PERF_MULTI_TENANCY_CACHE_MAX') ?? + positiveNonZeroInt(env.PERF_GRAPHILE_MULTI_TENANCY_CACHE_MAX, 'PERF_GRAPHILE_MULTI_TENANCY_CACHE_MAX'); + if (multiTenancyCacheMax !== undefined) cacheSizes.multiTenancyCacheMax = multiTenancyCacheMax; + const pgCacheMax = positiveNonZeroInt(env.PERF_PG_CACHE_MAX, 'PERF_PG_CACHE_MAX'); + if (pgCacheMax !== undefined) cacheSizes.pgCacheMax = pgCacheMax; + if (Object.keys(cacheSizes).length > 0) overlay.cacheSizes = cacheSizes; + + const failFast = boolEnv(env.PERF_FAIL_FAST); + if (failFast !== undefined) overlay.failFast = failFast; + const captureMemory = boolEnv(env.PERF_CAPTURE_MEMORY); + if (captureMemory !== undefined) overlay.captureMemory = captureMemory; + + const routeProbeSampleSize = positiveInt(env.PERF_ROUTE_PROBE_SAMPLE_SIZE, 'PERF_ROUTE_PROBE_SAMPLE_SIZE'); + if (routeProbeSampleSize !== undefined) overlay.routeProbeSampleSize = routeProbeSampleSize; + const errorSampleLimit = positiveInt(env.PERF_ERROR_SAMPLE_LIMIT, 'PERF_ERROR_SAMPLE_LIMIT'); + if (errorSampleLimit !== undefined) overlay.errorSampleLimit = errorSampleLimit; + const testTimeoutMs = positiveInt(env.PERF_TEST_TIMEOUT_MS, 'PERF_TEST_TIMEOUT_MS'); + if (testTimeoutMs !== undefined) overlay.testTimeoutMs = testTimeoutMs; + + if (env.PERF_CONSTRUCTIVE_LOCAL_PATH) { + overlay.constructiveLocalPath = env.PERF_CONSTRUCTIVE_LOCAL_PATH; + } + + return overlay; +}; + +const collectEnvOverrideStrings = (env: NodeJS.ProcessEnv): Record => { + const result: Record = {}; + const cacheEnvKeys = new Set([ + 'GRAPHILE_CACHE_MAX', + 'GRAPHILE_MULTI_TENANCY_CACHE_MAX', + 'PG_CACHE_MAX', + ]); + for (const [key, value] of Object.entries(env)) { + if ((key.startsWith('PERF_') || cacheEnvKeys.has(key)) && value != null) result[key] = value; + } + return result; +}; + +const cacheSizeFromDirectEnv = (env: NodeJS.ProcessEnv): CacheSizeConfig => { + const cacheSizes: CacheSizeConfig = {}; + const graphileCacheMax = positiveNonZeroInt(env.GRAPHILE_CACHE_MAX, 'GRAPHILE_CACHE_MAX'); + if (graphileCacheMax !== undefined) cacheSizes.graphileCacheMax = graphileCacheMax; + const multiTenancyCacheMax = positiveNonZeroInt( + env.GRAPHILE_MULTI_TENANCY_CACHE_MAX, + 'GRAPHILE_MULTI_TENANCY_CACHE_MAX' + ); + if (multiTenancyCacheMax !== undefined) cacheSizes.multiTenancyCacheMax = multiTenancyCacheMax; + const pgCacheMax = positiveNonZeroInt(env.PG_CACHE_MAX, 'PG_CACHE_MAX'); + if (pgCacheMax !== undefined) cacheSizes.pgCacheMax = pgCacheMax; + return cacheSizes; +}; + +const resolveCacheSizes = ( + merged: PerfRunConfigOverlay, + env: NodeJS.ProcessEnv +): { cacheSizes: CacheSizeConfig; effectiveCacheEnv: Record } => { + const directEnv = cacheSizeFromDirectEnv(env); + const cacheSizes: CacheSizeConfig = { + ...directEnv, + ...merged.cacheSizes, + }; + const effectiveCacheEnv: Record = {}; + if (cacheSizes.graphileCacheMax !== undefined) { + effectiveCacheEnv.GRAPHILE_CACHE_MAX = String(cacheSizes.graphileCacheMax); + } + if (cacheSizes.multiTenancyCacheMax !== undefined) { + effectiveCacheEnv.GRAPHILE_MULTI_TENANCY_CACHE_MAX = String(cacheSizes.multiTenancyCacheMax); + } + if (cacheSizes.pgCacheMax !== undefined) { + effectiveCacheEnv.PG_CACHE_MAX = String(cacheSizes.pgCacheMax); + } + return { cacheSizes, effectiveCacheEnv }; +}; + +const sanitizePrefix = (value: string): string => value.replace(/[^a-zA-Z0-9_]/g, '_').slice(0, 48); + +const isConstructiveLocalPath = (candidate: string): boolean => + fs.existsSync(path.join(candidate, 'pgpm.plan')) && + fs.existsSync(path.join(candidate, 'constructive-local.control')); + +const ancestorDirs = (startDir: string): string[] => { + const dirs: string[] = []; + let current = path.resolve(startDir); + for (;;) { + dirs.push(current); + const parent = path.dirname(current); + if (parent === current) return dirs; + current = parent; + } +}; + +export const discoverConstructiveLocalPath = (input: { + cwd?: string; + env?: NodeJS.ProcessEnv; +} = {}): string => { + const env = input.env || process.env; + const override = env.PERF_CONSTRUCTIVE_LOCAL_PATH; + if (override) { + const resolved = path.resolve(override); + if (!isConstructiveLocalPath(resolved)) { + throw new Error( + `PERF_CONSTRUCTIVE_LOCAL_PATH does not look like constructive-local: ${resolved}. ` + + 'Expected pgpm.plan and constructive-local.control.' + ); + } + return resolved; + } + + const cwd = path.resolve(input.cwd || process.cwd()); + const candidates: string[] = []; + for (const dir of ancestorDirs(cwd)) { + candidates.push(path.join(dir, 'constructive-db', 'services', 'constructive-local')); + candidates.push(path.join(path.dirname(dir), 'constructive-db', 'services', 'constructive-local')); + if (path.basename(dir) === 'constructive') { + candidates.push(path.join(path.dirname(dir), 'constructive-db', 'services', 'constructive-local')); + } + } + + const seen = new Set(); + for (const candidate of candidates) { + const resolved = path.resolve(candidate); + if (seen.has(resolved)) continue; + seen.add(resolved); + if (isConstructiveLocalPath(resolved)) return resolved; + } + + throw new Error( + 'Could not find constructive-db/services/constructive-local. ' + + 'Set PERF_CONSTRUCTIVE_LOCAL_PATH to the local constructive-local service path.' + ); +}; + +export const loadPerfConfig = ( + env: NodeJS.ProcessEnv = process.env, + options: { cwd?: string } = {} +): PerfRunConfig => { + const runId = nowRunId(); + const configGroup = env.PERF_CONFIG_GROUP || DEFAULT_CONFIG_GROUP; + const groupOverlay = BUILT_IN_CONFIG_GROUPS[configGroup]; + if (!groupOverlay) { + throw new Error( + `Unknown PERF_CONFIG_GROUP ${configGroup}; expected one of ${Object.keys(BUILT_IN_CONFIG_GROUPS).join(', ')}` + ); + } + + const configPath = resolveEnvConfigPath(env); + const configOverlay = readConfigOverlay(configPath); + const envOverlay = envOverrides(env); + + const merged = deepMerge( + deepMerge( + deepMerge(defaults as Record, { ...groupOverlay, configGroup }), + configOverlay as Record | undefined + ), + envOverlay as Record + ) as PerfRunConfigOverlay; + + const scaleProfile = { + ...defaults.scaleProfile, + ...groupOverlay.scaleProfile, + ...configOverlay?.scaleProfile, + ...envOverlay.scaleProfile, + } as ScaleProfile; + const runDir = merged.runDir || path.join(DEFAULT_RUN_ROOT, `${configGroup}-${runId}`); + const constructiveLocalPath = merged.constructiveLocalPath + ? discoverConstructiveLocalPath({ + cwd: options.cwd, + env: { ...env, PERF_CONSTRUCTIVE_LOCAL_PATH: merged.constructiveLocalPath }, + }) + : discoverConstructiveLocalPath({ cwd: options.cwd, env }); + const { cacheSizes, effectiveCacheEnv } = resolveCacheSizes(merged, env); + + return { + schemaVersion: PERF_SCHEMA_VERSION, + runId, + name: merged.name || configGroup, + configGroup, + configPath, + runDir, + connectionPolicy: merged.connectionPolicy || 'reuse', + routingModes: (merged.routingModes || ['private']) as RoutingMode[], + cacheModes: (merged.cacheModes || ['new']) as CacheMode[], + scaleProfile, + workloadProfiles: { + private: merged.workloadProfiles?.private || 'metadata-read', + public: merged.workloadProfiles?.public || 'business-crud', + }, + publicPreflight: { + allowUnderProvisioned: merged.publicPreflight?.allowUnderProvisioned ?? false, + }, + cacheSizes, + effectiveCacheEnv, + failFast: merged.failFast ?? true, + captureMemory: merged.captureMemory ?? true, + routeProbeSampleSize: merged.routeProbeSampleSize ?? 1, + errorSampleLimit: merged.errorSampleLimit ?? 20, + testTimeoutMs: merged.testTimeoutMs ?? 120_000, + constructiveLocalPath, + benchmarkOwnedPrefix: sanitizePrefix(`perf_${runId}`), + source: { + defaults: 'built-in defaults', + group: configGroup, + configPath, + envOverrides: collectEnvOverrideStrings(env), + }, + }; +}; diff --git a/graphql/server-test/perf/src/context.ts b/graphql/server-test/perf/src/context.ts new file mode 100644 index 0000000000..f84d0a9188 --- /dev/null +++ b/graphql/server-test/perf/src/context.ts @@ -0,0 +1,199 @@ +import type { GetConnectionsResult } from '../../src/types'; + +import { + CONSTRUCTIVE_SCHEMAS, + DEFAULT_DATABASE_ID, + META_SCHEMAS, + privateServicesHeaders, + responseHasUnexpectedErrors, + executeGraphql, +} from './operations'; +import type { BenchmarkContext, MatrixCase, PerfRunConfig } from './types'; + +const constructiveLocalSeedAdapters = (config: PerfRunConfig) => { + const { seed } = require('../../src') as typeof import('../../src'); + return [seed.pgpm(config.constructiveLocalPath, true)]; +}; + +interface ManagedContext { + context: BenchmarkContext; + inUse: boolean; +} + +export class BenchmarkContextManager { + private readonly contexts = new Map(); + private nextId = 1; + private envPatched = false; + private readonly previousEnv = new Map(); + + constructor(private readonly config: PerfRunConfig) {} + + compatibilityKeyFor(matrixCase: MatrixCase): string { + return [ + `routing=${matrixCase.routingMode}`, + `cache=${matrixCase.cacheMode}`, + `cacheEnv=${JSON.stringify(this.config.effectiveCacheEnv)}`, + 'seed=constructive-local', + `memory=${this.config.captureMemory ? 'capture' : 'skip'}`, + ].join('|'); + } + + async acquire(matrixCase: MatrixCase): Promise { + const baseKey = this.compatibilityKeyFor(matrixCase); + const compatibilityKey = this.config.connectionPolicy === 'per-case' + ? `${baseKey}|case=${matrixCase.caseId}|seq=${this.nextId}` + : baseKey; + + const existing = this.config.connectionPolicy === 'reuse' ? this.contexts.get(compatibilityKey) : undefined; + if (existing) { + existing.context.reused = true; + existing.inUse = true; + return { ...existing.context, reused: true }; + } + + for (const key of [...this.contexts.keys()]) { + if (key !== compatibilityKey) { + await this.teardownContext(key); + } + } + + const context = await this.createContext(matrixCase, compatibilityKey); + this.contexts.set(compatibilityKey, { context, inUse: true }); + return context; + } + + async releaseCase(context: BenchmarkContext): Promise { + const managed = this.contexts.get(context.compatibilityKey); + if (managed) managed.inUse = false; + if (this.config.connectionPolicy === 'per-case') { + await this.teardownContext(context.compatibilityKey); + } + } + + async teardownAll(): Promise { + const keys = [...this.contexts.keys()]; + for (const key of keys) { + await this.teardownContext(key); + } + this.restoreRuntimeEnv(); + } + + private setPatchedEnv(key: string, value: string | undefined): void { + if (!this.previousEnv.has(key)) this.previousEnv.set(key, process.env[key]); + if (value === undefined) delete process.env[key]; + else process.env[key] = value; + } + + private patchRuntimeEnv(): void { + if (this.envPatched) return; + this.envPatched = true; + + for (const [key, value] of Object.entries(this.config.effectiveCacheEnv)) { + this.setPatchedEnv(key, value); + } + + if (this.config.captureMemory) { + if (process.env.NODE_ENV !== 'development') { + this.setPatchedEnv('NODE_ENV', 'development'); + } + if (!process.env.GRAPHQL_OBSERVABILITY_ENABLED) { + this.setPatchedEnv('GRAPHQL_OBSERVABILITY_ENABLED', 'true'); + } + } + } + + private restoreRuntimeEnv(): void { + if (!this.envPatched) return; + for (const [key, value] of this.previousEnv.entries()) { + if (value === undefined) delete process.env[key]; + else process.env[key] = value; + } + this.previousEnv.clear(); + this.envPatched = false; + } + + private async createContext(matrixCase: MatrixCase, compatibilityKey: string): Promise { + this.patchRuntimeEnv(); + + const { createSuperTestAgent, createTestServer, getConnections } = require('../../src') as typeof import('../../src'); + const { getEnvOptions } = require('@constructive-io/graphql-env') as typeof import('@constructive-io/graphql-env'); + const api = { + enableServicesApi: true, + isPublic: matrixCase.routingMode === 'public', + metaSchemas: META_SCHEMAS, + defaultDatabaseId: DEFAULT_DATABASE_ID, + useMultiTenancyCache: matrixCase.cacheMode === 'new', + } as any; + + const conn: GetConnectionsResult = await getConnections( + { + schemas: CONSTRUCTIVE_SCHEMAS, + authRole: 'anonymous', + server: { api }, + }, + constructiveLocalSeedAdapters(this.config) + ); + + let privateServer: any; + let privateRequest: any; + if (matrixCase.routingMode === 'public') { + privateServer = await createTestServer( + getEnvOptions({ + pg: conn.pg.config, + api: { + enableServicesApi: true, + isPublic: false, + metaSchemas: META_SCHEMAS, + defaultDatabaseId: DEFAULT_DATABASE_ID, + exposedSchemas: META_SCHEMAS, + anonRole: 'anonymous', + roleName: 'anonymous', + useMultiTenancyCache: matrixCase.cacheMode === 'new', + } as any, + }) + ); + privateRequest = createSuperTestAgent(privateServer); + } + + const id = `ctx-${this.nextId++}`; + const context: BenchmarkContext = { + id, + compatibilityKey, + reused: false, + createdAt: new Date().toISOString(), + serverUrl: conn.server.url, + graphqlUrl: conn.server.graphqlUrl, + conn, + privateServer, + privateRequest, + privateServerUrl: privateServer?.url, + }; + + await this.verifyConstructiveLocal(context); + return context; + } + + private async verifyConstructiveLocal(context: BenchmarkContext): Promise { + const response = await executeGraphql(context, { + query: '{ __typename }', + headers: privateServicesHeaders(), + surface: context.privateRequest ? 'private' : undefined, + }); + if (responseHasUnexpectedErrors(response)) { + throw new Error( + `constructive-local baseline did not expose private services GraphQL; ` + + `status=${response.status}; body=${JSON.stringify(response.body || response.text).slice(0, 1000)}` + ); + } + } + + private async teardownContext(key: string): Promise { + const managed = this.contexts.get(key); + if (!managed) return; + this.contexts.delete(key); + if (managed.context.privateServer) { + await managed.context.privateServer.stop(); + } + await managed.context.conn.teardown(); + } +} diff --git a/graphql/server-test/perf/src/dbpm-provision.ts b/graphql/server-test/perf/src/dbpm-provision.ts new file mode 100644 index 0000000000..38af918ab1 --- /dev/null +++ b/graphql/server-test/perf/src/dbpm-provision.ts @@ -0,0 +1,492 @@ +import { toRedactedErrorSample } from './artifacts'; +import { + executeGraphql, + privateModulesHeaders, + privateServicesHeaders, + publicHostHeaders, + responseErrorSample, + responseHasUnexpectedErrors, +} from './operations'; +import { buildPublicOperationProfiles } from './operations'; +import type { + BenchmarkContext, + DbpmProvisionResult, + MatrixCase, + PerfRunConfig, + RedactedErrorSample, + RequestProfile, +} from './types'; + +interface DbpmNode { + id: string; + databaseName: string; + ownerId: string; + subdomain: string; + domain: string; + status: string; + errorMessage?: string | null; + databaseId?: string | null; + completedAt?: string | null; +} + +interface ApiNode { + id: string; + databaseId: string; + name: string; + isPublic: boolean; +} + +interface DomainNode { + id: string; + databaseId: string; + apiId?: string | null; + subdomain?: string | null; + domain?: string | null; +} + +interface SchemaNode { + id: string; + databaseId: string; + name: string; + schemaName: string; + isPublic: boolean; +} + +interface ServicesSnapshot { + apis?: { nodes: ApiNode[] }; + domains?: { nodes: DomainNode[] }; + schemas?: { nodes: SchemaNode[] }; +} + +const SUDO_USER_ID = '00000000-0000-0000-0000-000000000002'; +const DBPM_MODULES = ['users_module']; + +const createDatabaseProvisionModuleMutation = `mutation PerfCreateDbpm($input: CreateDatabaseProvisionModuleInput!) { + createDatabaseProvisionModule(input: $input) { + databaseProvisionModule { + id + databaseName + ownerId + subdomain + domain + status + errorMessage + databaseId + completedAt + } + } +}`; + +const servicesSnapshotQuery = `query PerfServicesSnapshot($first: Int!) { + apis(first: $first) { nodes { id databaseId name isPublic } } + domains(first: $first) { nodes { id databaseId apiId subdomain domain } } + schemas(first: $first) { nodes { id databaseId name schemaName isPublic } } +}`; + +const provisionTableMutation = `mutation PerfProvisionTable($input: ProvisionTableInput!) { + provisionTable(input: $input) { + result { outTableId outFields } + } +}`; + +const publicSchemaIntrospectionQuery = `fragment PerfPublicTypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } +} + +query PerfPublicSchemaShape { + __schema { + queryType { fields { name } } + mutationType { + fields { + name + args { name type { ...PerfPublicTypeRef } } + type { + kind + name + fields { name } + ofType { + kind + name + fields { name } + ofType { + kind + name + fields { name } + } + } + } + } + } + } +}`; + +const toHost = (domain: DomainNode): string | null => { + if (!domain.domain) return null; + return domain.subdomain ? `${domain.subdomain}.${domain.domain}` : domain.domain; +}; + +const safeLower = (value: string): string => + value + .replace(/[^a-zA-Z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .toLowerCase(); + +const safeDbName = (value: string): string => + value + .replace(/[^a-zA-Z0-9_]+/g, '_') + .replace(/^_+|_+$/g, '') + .toLowerCase(); + +const trimToken = (value: string, maxLength: number): string => + value.slice(0, maxLength).replace(/[-_]+$/g, ''); + +const uniqueBase = (config: PerfRunConfig, matrixCase: MatrixCase, index: number): string => + trimToken(safeLower(`${config.benchmarkOwnedPrefix}-${index + 1}-${matrixCase.caseId}`), 48); + +const tableStem = (base: string): string => trimToken(safeDbName(`perf_items_${base}`), 60); + +const namedTypeName = (typeRef: any): string | undefined => + typeRef?.name || (typeRef?.ofType ? namedTypeName(typeRef.ofType) : undefined); + +const fieldsForType = (typeRef: any): string[] => { + if (!typeRef) return []; + if (Array.isArray(typeRef.fields)) return typeRef.fields.map((field: any) => field.name).filter(Boolean); + return fieldsForType(typeRef.ofType); +}; + +const nodeFieldFromListField = (listField?: string): string | undefined => { + if (!listField) return undefined; + return listField.endsWith('s') && listField.length > 1 ? listField.slice(0, -1) : listField; +}; + +const queryServicesSnapshot = async ( + context: BenchmarkContext +): Promise<{ snapshot?: ServicesSnapshot; errors: RedactedErrorSample[] }> => { + const response = await executeGraphql(context, { + query: servicesSnapshotQuery, + variables: { first: 1000 }, + headers: privateServicesHeaders(), + surface: 'private', + }); + + if (responseHasUnexpectedErrors(response)) { + return { errors: [responseErrorSample(response, { operation: 'dbpm.snapshot.private' })] }; + } + + return { snapshot: response.body.data, errors: [] }; +}; + +const apiHostFor = (snapshot: ServicesSnapshot, databaseId: string): { host?: string; api?: ApiNode; domain?: DomainNode } => { + const apis = snapshot.apis?.nodes || []; + const domains = snapshot.domains?.nodes || []; + const api = apis.find((candidate) => candidate.databaseId === databaseId && candidate.name === 'api' && candidate.isPublic); + const domain = domains.find((candidate) => candidate.databaseId === databaseId && candidate.apiId === api?.id); + return { api, domain, host: domain ? toHost(domain) || undefined : undefined }; +}; + +const appPublicSchemaFor = (snapshot: ServicesSnapshot, databaseId: string): SchemaNode | undefined => + (snapshot.schemas?.nodes || []).find((schema) => databaseId === schema.databaseId && schema.name === 'app_public'); + +const introspectPublicTableShape = async (input: { + context: BenchmarkContext; + host: string; + tableName: string; +}): Promise<{ + listField?: string; + createField?: string; + updateField?: string; + createInputType?: string; + updateInputType?: string; + nodeField?: string; + patchField?: string; + errors: RedactedErrorSample[]; +}> => { + const { context, host, tableName } = input; + const response = await executeGraphql<{ + __schema?: { + queryType?: { fields?: Array<{ name: string }> }; + mutationType?: { + fields?: Array<{ + name: string; + args?: Array<{ name: string; type?: unknown }>; + type?: unknown; + }>; + }; + }; + }>(context, { + query: publicSchemaIntrospectionQuery, + headers: publicHostHeaders(host), + }); + + if (responseHasUnexpectedErrors(response)) { + return { errors: [responseErrorSample(response, { operation: 'dbpm.public.introspect' })] }; + } + + const normalizedTable = tableName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); + const queryFields = response.body.data?.__schema?.queryType?.fields?.map((field) => field.name) || []; + const mutationFields = response.body.data?.__schema?.mutationType?.fields || []; + const includesTable = (fieldName: string) => + fieldName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase().includes(normalizedTable); + const listField = queryFields.find((fieldName) => includesTable(fieldName)); + const createMutation = mutationFields.find((field) => field.name.startsWith('create') && includesTable(field.name)); + const updateMutation = mutationFields.find((field) => field.name.startsWith('update') && includesTable(field.name)); + const payloadFields = fieldsForType(createMutation?.type).length > 0 + ? fieldsForType(createMutation?.type) + : fieldsForType(updateMutation?.type); + const nodeField = + payloadFields.find((fieldName) => includesTable(fieldName)) || + nodeFieldFromListField(listField); + const inputTypeFor = (field?: { args?: Array<{ name: string; type?: unknown }> }): string | undefined => + namedTypeName(field?.args?.find((arg) => arg.name === 'input')?.type); + + return { + listField, + createField: createMutation?.name, + updateField: updateMutation?.name, + createInputType: inputTypeFor(createMutation), + updateInputType: inputTypeFor(updateMutation), + nodeField, + patchField: nodeField ? `${nodeField}Patch` : undefined, + errors: [], + }; +}; + +const provisionOneTenant = async (input: { + context: BenchmarkContext; + config: PerfRunConfig; + matrixCase: MatrixCase; + index: number; +}): Promise<{ + requestProfile?: RequestProfile; + objectIds: string[]; + businessTableCreated: boolean; + errors: RedactedErrorSample[]; + warnings: string[]; +}> => { + const { context, config, matrixCase, index } = input; + const errors: RedactedErrorSample[] = []; + const warnings: string[] = []; + const objectIds: string[] = []; + const base = uniqueBase(config, matrixCase, index); + const subdomain = trimToken(`perf-${base}`, 60); + const databaseName = trimToken(safeDbName(`perf_dbpm_${base}`), 60); + const tableName = tableStem(base); + + const createDbpm = await executeGraphql<{ createDatabaseProvisionModule?: { databaseProvisionModule?: DbpmNode } }>( + context, + { + query: createDatabaseProvisionModuleMutation, + variables: { + input: { + databaseProvisionModule: { + databaseName, + ownerId: SUDO_USER_ID, + subdomain, + domain: 'localhost', + modules: DBPM_MODULES, + options: {}, + bootstrapUser: false, + }, + }, + }, + headers: privateModulesHeaders(), + surface: 'private', + } + ); + + const provision = createDbpm.body.data?.createDatabaseProvisionModule?.databaseProvisionModule; + if (responseHasUnexpectedErrors(createDbpm) || !provision?.databaseId || provision.status !== 'completed') { + errors.push(responseErrorSample(createDbpm, { operation: 'dbpm.createDatabaseProvisionModule' })); + if (provision?.errorMessage) { + errors.push( + toRedactedErrorSample(provision.errorMessage, { operation: 'dbpm.createDatabaseProvisionModule.errorMessage' }) as RedactedErrorSample + ); + } + return { objectIds, businessTableCreated: false, errors, warnings }; + } + + objectIds.push(provision.id, provision.databaseId); + + const snapshotResult = await queryServicesSnapshot(context); + errors.push(...snapshotResult.errors); + const snapshot = snapshotResult.snapshot; + if (!snapshot) { + return { objectIds, businessTableCreated: false, errors, warnings }; + } + + const appSchema = appPublicSchemaFor(snapshot, provision.databaseId); + const route = apiHostFor(snapshot, provision.databaseId); + if (!appSchema?.id) { + errors.push(toRedactedErrorSample('DBPM provision did not expose an app_public schema.', { + operation: 'dbpm.findAppPublicSchema', + }) as RedactedErrorSample); + } + if (!route.host || !route.api?.id || !route.domain?.id) { + errors.push(toRedactedErrorSample('DBPM provision did not expose an api public host.', { + operation: 'dbpm.findApiHost', + }) as RedactedErrorSample); + } + if (!appSchema?.id || !route.host || !route.api || !route.domain) { + return { objectIds, businessTableCreated: false, errors, warnings }; + } + + const table = await executeGraphql<{ provisionTable?: { result?: Array<{ outTableId?: string; outFields?: string[] }> } }>( + context, + { + query: provisionTableMutation, + variables: { + input: { + databaseId: provision.databaseId, + schemaId: appSchema.id, + tableName, + nodes: [{ $type: 'DataId' }], + fields: [{ name: 'label', type: { name: 'text' } }], + useRls: false, + grants: [ + { + roles: ['anonymous', 'authenticated'], + privileges: [['select', '*'], ['insert', '*'], ['update', '*'], ['delete', '*']], + }, + ], + }, + }, + headers: privateModulesHeaders(), + surface: 'private', + } + ); + + const tableResult = table.body.data?.provisionTable?.result?.[0]; + if (responseHasUnexpectedErrors(table) || !tableResult?.outTableId) { + errors.push(responseErrorSample(table, { operation: 'dbpm.provisionTable' })); + return { objectIds, businessTableCreated: false, errors, warnings }; + } + + objectIds.push(tableResult.outTableId, ...(tableResult.outFields || [])); + + const publicShape = await introspectPublicTableShape({ context, host: route.host, tableName }); + errors.push(...publicShape.errors); + if (!publicShape.listField) { + errors.push(toRedactedErrorSample(`Public API host ${route.host} did not expose benchmark table ${tableName}.`, { + operation: 'dbpm.public.tableShape', + }) as RedactedErrorSample); + return { objectIds, businessTableCreated: true, errors, warnings }; + } + if ( + !publicShape.createField || + !publicShape.updateField || + !publicShape.createInputType || + !publicShape.updateInputType || + !publicShape.nodeField || + !publicShape.patchField + ) { + warnings.push(`Public API host ${route.host} exposes list field ${publicShape.listField} but not full CRUD mutations.`); + } + + return { + objectIds, + businessTableCreated: true, + errors, + warnings, + requestProfile: { + id: `public-dbpm-${index + 1}`, + routingMode: 'public', + routeKey: route.host, + headers: publicHostHeaders(route.host), + description: 'Benchmark-owned public DBPM host route provisioned through constructive-local private GraphQL.', + benchmarkOwned: true, + metadata: { + source: 'constructive-local-dbpm-graphql', + host: route.host, + apiId: route.api.id, + domainId: route.domain.id, + databaseId: provision.databaseId, + schemaId: appSchema.id, + schemaName: appSchema.schemaName, + tableName, + tableId: tableResult.outTableId, + outFieldIds: tableResult.outFields || [], + listField: publicShape.listField, + createField: publicShape.createField, + updateField: publicShape.updateField, + createInputType: publicShape.createInputType, + updateInputType: publicShape.updateInputType, + nodeField: publicShape.nodeField, + patchField: publicShape.patchField, + }, + }, + }; +}; + +export const provisionDbpmPublic = async (input: { + context: BenchmarkContext; + matrixCase: MatrixCase; + config: PerfRunConfig; +}): Promise => { + const { context, matrixCase, config } = input; + const errors: RedactedErrorSample[] = []; + const warnings: string[] = []; + const requestProfiles: RequestProfile[] = []; + const objectIds: string[] = []; + let businessTableCount = 0; + + if (!context.privateRequest) { + const error = toRedactedErrorSample('Private admin GraphQL server is unavailable for DBPM provision.', { + operation: 'dbpm.privateAdmin', + }) as RedactedErrorSample; + return { + ok: false, + completed: false, + source: 'unavailable', + tenantCount: 0, + publicHostCount: 0, + apiCount: 0, + businessTableCount: 0, + requestProfiles, + operationProfiles: buildPublicOperationProfiles(matrixCase.workloadProfile), + benchmarkOwnedObjectIds: [], + errors: [error], + warnings, + }; + } + + for (let index = 0; index < matrixCase.scaleProfile.k; index += 1) { + const provisioned = await provisionOneTenant({ context, config, matrixCase, index }); + objectIds.push(...provisioned.objectIds); + errors.push(...provisioned.errors); + warnings.push(...provisioned.warnings); + if (provisioned.businessTableCreated) businessTableCount += 1; + if (provisioned.requestProfile) requestProfiles.push(provisioned.requestProfile); + } + + if (requestProfiles.length < matrixCase.scaleProfile.k) { + warnings.push(`Requested k=${matrixCase.scaleProfile.k} public profiles but provisioned ${requestProfiles.length}.`); + } + + return { + ok: errors.length === 0 && requestProfiles.length >= matrixCase.scaleProfile.k, + completed: errors.length === 0 && requestProfiles.length >= matrixCase.scaleProfile.k, + source: requestProfiles.length > 0 ? 'graphql' : 'unavailable', + tenantCount: new Set(requestProfiles.map((profile) => String(profile.metadata.databaseId))).size, + publicHostCount: requestProfiles.length, + apiCount: requestProfiles.length, + businessTableCount, + requestProfiles, + operationProfiles: buildPublicOperationProfiles(matrixCase.workloadProfile), + benchmarkOwnedObjectIds: objectIds, + errors, + warnings, + }; +}; diff --git a/graphql/server-test/perf/src/gates.ts b/graphql/server-test/perf/src/gates.ts new file mode 100644 index 0000000000..ea00f1a0a2 --- /dev/null +++ b/graphql/server-test/perf/src/gates.ts @@ -0,0 +1,34 @@ +import type { LoadReport, MemoryCaseArtifacts, RouteProbeSummary } from './types'; + +export const collectHardGateFailures = (input: { + setupFailures?: string[]; + routeProbe?: RouteProbeSummary; + load?: LoadReport; + memory?: MemoryCaseArtifacts; + captureMemory: boolean; + reportsWritten?: boolean; +}): string[] => { + const failures = [...(input.setupFailures || [])]; + + if (input.routeProbe) { + if (!input.routeProbe.ok) failures.push('route probe failed'); + if (input.routeProbe.unexpectedGraphqlErrors !== 0) failures.push('route probe returned unexpected GraphQL errors'); + } + + if (input.load) { + if (input.load.failedRequests !== 0) failures.push(`measured load had ${input.load.failedRequests} failed requests`); + if (input.load.unexpectedGraphqlErrors !== 0) { + failures.push(`measured load had ${input.load.unexpectedGraphqlErrors} unexpected GraphQL errors`); + } + if (input.load.failFast?.triggered) failures.push(input.load.failFast.reason || 'fail-fast triggered'); + } + + if (input.captureMemory && input.memory) { + if (!input.memory.beforeOk) failures.push('memory before capture failed'); + if (!input.memory.afterOk) failures.push('memory after capture failed'); + } + + if (input.reportsWritten === false) failures.push('case report write failed'); + + return failures; +}; diff --git a/graphql/server-test/perf/src/load.ts b/graphql/server-test/perf/src/load.ts new file mode 100644 index 0000000000..a9491da786 --- /dev/null +++ b/graphql/server-test/perf/src/load.ts @@ -0,0 +1,124 @@ +import { toRedactedErrorSample } from './artifacts'; +import { buildOperationBag, compatibleOperationsFor, executeOperation } from './operations'; +import { summarizeOutcomes } from './stats'; +import type { + BenchmarkContext, + LoadReport, + MatrixCase, + PerfRunConfig, + RequestOutcome, + RequestProfile, + RuntimeOperationProfile, +} from './types'; + +export const runMeasuredLoad = async (input: { + context: BenchmarkContext; + matrixCase: MatrixCase; + config: PerfRunConfig; + requestProfiles: RequestProfile[]; + operationProfiles: RuntimeOperationProfile[]; +}): Promise => { + const { context, matrixCase, config, requestProfiles, operationProfiles } = input; + const durationMs = Math.max(0, matrixCase.scaleProfile.durationSeconds * 1000); + const workers = Math.max(1, matrixCase.scaleProfile.workers); + const endAt = performance.now() + durationMs; + const outcomes: RequestOutcome[] = []; + const operationBag = buildOperationBag(operationProfiles); + const rowIdsByProfile = new Map(); + let sequence = 0; + let stopReason: string | undefined; + let consecutiveFailures = 0; + + const nextSequence = (): number => sequence++; + + const rowBucketFor = (requestProfile: RequestProfile): string[] => { + let bucket = rowIdsByProfile.get(requestProfile.id); + if (!bucket) { + bucket = []; + rowIdsByProfile.set(requestProfile.id, bucket); + } + return bucket; + }; + + const maybeFailFast = (): void => { + if (!config.failFast || stopReason) return; + const total = outcomes.length; + const failed = outcomes.filter((outcome) => !outcome.ok).length; + if (consecutiveFailures >= 10) { + stopReason = `fail-fast: ${consecutiveFailures} consecutive request failures`; + return; + } + if (total >= 20 && failed / total > 0.5) { + stopReason = `fail-fast: request failure rate ${(failed / total).toFixed(2)} after ${total} requests`; + } + }; + + const worker = async (): Promise => { + while (!stopReason && performance.now() < endAt) { + const seq = nextSequence(); + const requestProfile = requestProfiles[seq % requestProfiles.length]; + const compatible = compatibleOperationsFor(requestProfile, operationBag); + const rowBucket = rowBucketFor(requestProfile); + let operation = compatible[seq % Math.max(1, compatible.length)]; + if (operation?.requiresExistingRow && rowBucket.length === 0) { + operation = compatible.find((candidate) => candidate.producesRowId && !candidate.requiresExistingRow) || operation; + } + const rowId = rowBucket.length > 0 ? rowBucket[seq % rowBucket.length] : undefined; + let outcome: RequestOutcome; + if (!operation) { + outcome = { + ok: false, + latencyMs: 0, + operation: 'load.selectOperation', + requestProfileId: requestProfile.id, + unexpectedGraphqlErrors: 0, + error: toRedactedErrorSample(`No compatible operation for ${requestProfile.id}`, { + operation: 'load.selectOperation', + requestProfileId: requestProfile.id, + }), + }; + } else { + outcome = await executeOperation({ + context, + requestProfile, + operation, + sequence: seq, + rowId, + config, + matrixCase, + }); + } + if (outcome.ok && outcome.rowId && !rowBucket.includes(outcome.rowId)) { + rowBucket.push(outcome.rowId); + } + outcomes.push(outcome); + consecutiveFailures = outcome.ok ? 0 : consecutiveFailures + 1; + maybeFailFast(); + } + }; + + const started = performance.now(); + await Promise.all(Array.from({ length: workers }, () => worker())); + const elapsed = performance.now() - started; + const report = summarizeOutcomes({ + outcomes, + durationMs: elapsed, + durationSeconds: matrixCase.scaleProfile.durationSeconds, + workers, + failFast: stopReason ? { triggered: true, reason: stopReason } : { triggered: false }, + errorSampleLimit: config.errorSampleLimit, + }); + + if (report.totalRequests === 0) { + return { + ...report, + ok: false, + failFast: { + triggered: true, + reason: 'measured load completed without issuing any requests', + }, + }; + } + + return report; +}; diff --git a/graphql/server-test/perf/src/matrix.ts b/graphql/server-test/perf/src/matrix.ts new file mode 100644 index 0000000000..e1b18f21b6 --- /dev/null +++ b/graphql/server-test/perf/src/matrix.ts @@ -0,0 +1,300 @@ +import { + caseReportPath, + createRunArtifactPaths, + ensureRunDirs, + errorsReportPath, + writeJsonArtifact, +} from './artifacts'; +import { BenchmarkContextManager } from './context'; +import { collectHardGateFailures } from './gates'; +import { runMeasuredLoad } from './load'; +import { captureMemorySnapshot } from './memory'; +import { buildPrivateProfiles } from './profiles'; +import { probeProfiles, runPublicPreflight } from './preflight'; +import { resetBenchmarkOwnedState } from './reset'; +import type { + BenchmarkContext, + CaseReport, + CaseSummary, + MatrixCase, + PerfRunConfig, + PerfRunSummary, + PublicPreflightResult, + RequestProfile, + RouteProbeSummary, + RunArtifactPaths, + RuntimeOperationProfile, +} from './types'; +import { PERF_SCHEMA_VERSION } from './types'; + +const sanitizeCaseId = (value: string): string => + value.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^-+|-+$/g, '').toLowerCase(); + +export const expandMatrix = (config: PerfRunConfig): MatrixCase[] => { + const cases: MatrixCase[] = []; + for (const routingMode of config.routingModes) { + for (const cacheMode of config.cacheModes) { + const workloadProfile = config.workloadProfiles[routingMode]; + const caseId = sanitizeCaseId(`${routingMode}-${cacheMode}-${config.scaleProfile.name}-${workloadProfile}`); + cases.push({ + caseId, + routingMode, + cacheMode, + scaleProfile: config.scaleProfile, + workloadProfile, + mutates: false, + }); + } + } + return cases; +}; + +const emptyRouteProbe = (message: string): RouteProbeSummary => ({ + ok: false, + attempted: 0, + succeeded: 0, + failed: 0, + unexpectedGraphqlErrors: 0, + errorSamples: [{ at: new Date().toISOString(), operation: 'routeProbe', message }], +}); + +const reportToSummary = (report: CaseReport): CaseSummary => ({ + caseId: report.caseId, + status: report.status, + ok: report.ok, + routingMode: report.matrix.routingMode, + cacheMode: report.matrix.cacheMode, + workloadProfile: report.matrix.workloadProfile, + contextId: report.lifecycle.contextId, + totalRequests: report.load?.totalRequests, + failedRequests: report.load?.failedRequests, + unexpectedGraphqlErrors: report.load?.unexpectedGraphqlErrors, + qps: report.load?.qps, + hardGateFailures: report.gates.hardGateFailures, + caseReportPath: report.artifacts.caseReportPath, +}); + +const countDistinctMetadata = (profiles: RequestProfile[], key: string): number => + new Set( + profiles + .map((profile) => profile.metadata[key]) + .filter((value): value is string => typeof value === 'string' && value.length > 0) + ).size; + +const writeErrorArtifact = async (input: { + artifacts: RunArtifactPaths; + matrixCase: MatrixCase; + hardGateFailures: string[]; + routeProbe?: RouteProbeSummary; + preflight?: PublicPreflightResult; + load?: CaseReport['load']; + caughtError?: unknown; +}): Promise => { + const targetPath = errorsReportPath(input.artifacts, input.matrixCase.caseId); + await writeJsonArtifact(targetPath, { + schemaVersion: PERF_SCHEMA_VERSION, + caseId: input.matrixCase.caseId, + writtenAt: new Date().toISOString(), + hardGateFailures: input.hardGateFailures, + routeProbeErrors: input.routeProbe?.errorSamples || [], + preflightErrors: input.preflight?.errorSamples || [], + loadErrors: input.load?.errorSamples || [], + caughtError: input.caughtError + ? { + message: input.caughtError instanceof Error ? input.caughtError.message : String(input.caughtError), + stack: input.caughtError instanceof Error ? input.caughtError.stack : undefined, + } + : undefined, + }); + return targetPath; +}; + +const runCase = async (input: { + manager: BenchmarkContextManager; + config: PerfRunConfig; + artifacts: RunArtifactPaths; + matrixCase: MatrixCase; +}): Promise => { + const { manager, config, artifacts, matrixCase } = input; + const startedAt = new Date().toISOString(); + const reportPath = caseReportPath(artifacts, matrixCase.caseId); + let context: BenchmarkContext | undefined; + let routeProbe: RouteProbeSummary = emptyRouteProbe('case did not reach route probe'); + let preflight: PublicPreflightResult | undefined; + let requestProfiles: RequestProfile[] = []; + let operationProfiles: RuntimeOperationProfile[] = []; + let load: CaseReport['load']; + let memory: CaseReport['memory'] = { beforeOk: false, afterOk: false }; + let caughtError: unknown; + const setupFailures: string[] = []; + + try { + context = await manager.acquire(matrixCase); + + if (matrixCase.routingMode === 'public') { + preflight = await runPublicPreflight({ context, matrixCase, config, artifacts }); + requestProfiles = preflight.requestProfiles; + operationProfiles = preflight.operationProfiles; + routeProbe = preflight.routeProbe; + setupFailures.push(...preflight.hardGateFailures); + } else { + const built = buildPrivateProfiles(matrixCase, config); + requestProfiles = built.requestProfiles; + operationProfiles = built.operationProfiles; + routeProbe = await probeProfiles({ context, matrixCase, config, requestProfiles, operationProfiles }); + } + + const before = await captureMemorySnapshot({ context, config, artifacts, caseId: matrixCase.caseId, phase: 'before' }); + memory = { ...memory, beforePath: before.path, beforeOk: before.ok }; + + const readyForLoad = setupFailures.length === 0 && routeProbe.ok && requestProfiles.length > 0 && operationProfiles.length > 0; + if (readyForLoad) { + const reset = await resetBenchmarkOwnedState({ context, matrixCase, config }); + setupFailures.push(...reset.hardGateFailures); + if (reset.ok) { + const finalProbe = await probeProfiles({ context, matrixCase, config, requestProfiles, operationProfiles }); + routeProbe = finalProbe; + if (finalProbe.ok) { + load = await runMeasuredLoad({ context, matrixCase, config, requestProfiles, operationProfiles }); + } + } + } + + const after = await captureMemorySnapshot({ context, config, artifacts, caseId: matrixCase.caseId, phase: 'after' }); + memory = { ...memory, afterPath: after.path, afterOk: after.ok }; + } catch (error) { + caughtError = error; + setupFailures.push(error instanceof Error ? error.message : String(error)); + } + + const hardGateFailures = collectHardGateFailures({ + setupFailures, + routeProbe, + load, + memory, + captureMemory: config.captureMemory, + }); + + const errorsPath = await writeErrorArtifact({ + artifacts, + matrixCase, + hardGateFailures, + routeProbe, + preflight, + load, + caughtError, + }); + + const ok = hardGateFailures.length === 0; + const report: CaseReport = { + schemaVersion: PERF_SCHEMA_VERSION, + runId: config.runId, + caseId: matrixCase.caseId, + startedAt, + finishedAt: new Date().toISOString(), + ok, + status: ok ? 'passed' : 'failed', + matrix: { + routingMode: matrixCase.routingMode, + cacheMode: matrixCase.cacheMode, + scaleProfile: matrixCase.scaleProfile, + workloadProfile: matrixCase.workloadProfile, + }, + lifecycle: { + connectionPolicy: config.connectionPolicy, + contextId: context?.id || 'unavailable', + contextReused: context?.reused || false, + compatibilityKey: context?.compatibilityKey || manager.compatibilityKeyFor(matrixCase), + serverUrl: context?.serverUrl || '', + }, + ...(preflight && { + preflight: { + ok: preflight.ok, + artifactPath: preflight.artifactPath, + provisionedTenantCount: preflight.provisionedTenantCount, + publicHostCount: preflight.publicHostCount, + requestProfileCount: preflight.requestProfileCount, + operationProfileCount: preflight.operationProfileCount, + hardGateFailures: preflight.hardGateFailures, + }, + }), + routeProbe, + ...(load && { load }), + memory, + gates: { + hardGateFailures, + observations: { + qps: load?.qps, + latencyMs: load?.latencyMs, + requestProfileCount: requestProfiles.length, + operationProfileCount: operationProfiles.length, + routeKeyCount: new Set(requestProfiles.map((profile) => profile.routeKey)).size, + expectedBuildKeyGroupCount: countDistinctMetadata(requestProfiles, 'expectedBuildKeyGroup'), + cacheKeyModes: [...new Set(requestProfiles.map((profile) => profile.metadata.cacheKeyMode).filter(Boolean))], + cacheSizes: config.cacheSizes, + effectiveCacheEnv: config.effectiveCacheEnv, + }, + }, + artifacts: { + caseReportPath: reportPath, + preflightPath: preflight?.artifactPath, + memoryBeforePath: memory.beforePath, + memoryAfterPath: memory.afterPath, + errorsPath, + }, + }; + + await writeJsonArtifact(reportPath, report); + if (context) await manager.releaseCase(context); + return report; +}; + +export const runPerfMatrix = async (config: PerfRunConfig): Promise => { + const startedAt = new Date().toISOString(); + const artifacts = createRunArtifactPaths(config.runDir); + await ensureRunDirs(artifacts); + const manager = new BenchmarkContextManager(config); + const cases = expandMatrix(config); + const caseReports: CaseReport[] = []; + + try { + for (const matrixCase of cases) { + caseReports.push(await runCase({ manager, config, artifacts, matrixCase })); + } + } finally { + await manager.teardownAll(); + } + + const summaries = caseReports.map(reportToSummary); + const passed = summaries.filter((summary) => summary.status === 'passed').length; + const failed = summaries.filter((summary) => summary.status === 'failed').length; + const skipped = summaries.filter((summary) => summary.status === 'skipped').length; + const summary: PerfRunSummary = { + schemaVersion: PERF_SCHEMA_VERSION, + runId: config.runId, + runDir: artifacts.runDir, + configGroup: config.configGroup, + configPath: config.configPath, + startedAt, + finishedAt: new Date().toISOString(), + pass: failed === 0 && skipped === 0, + config, + totals: { + caseCount: cases.length, + passed, + failed, + skipped, + }, + cases: summaries, + artifacts: { + summaryPath: artifacts.summaryPath, + casesDir: artifacts.casesDir, + preflightDir: artifacts.preflightDir, + memoryDir: artifacts.memoryDir, + errorsDir: artifacts.errorsDir, + }, + }; + + await writeJsonArtifact(artifacts.summaryPath, summary); + return summary; +}; diff --git a/graphql/server-test/perf/src/memory.ts b/graphql/server-test/perf/src/memory.ts new file mode 100644 index 0000000000..b6412b1b25 --- /dev/null +++ b/graphql/server-test/perf/src/memory.ts @@ -0,0 +1,66 @@ +import { toRedactedErrorSample, writeJsonArtifact } from './artifacts'; +import type { + BenchmarkContext, + MemoryPhase, + MemorySnapshotArtifact, + PerfRunConfig, + RunArtifactPaths, +} from './types'; +import { PERF_SCHEMA_VERSION } from './types'; +import { memoryReportPath } from './artifacts'; + +export const captureMemorySnapshot = async (input: { + context: BenchmarkContext; + config: PerfRunConfig; + artifacts: RunArtifactPaths; + caseId: string; + phase: MemoryPhase; +}): Promise<{ path: string; ok: boolean }> => { + const { context, config, artifacts, caseId, phase } = input; + const targetPath = memoryReportPath(artifacts, caseId, phase); + const base: Omit = { + schemaVersion: PERF_SCHEMA_VERSION, + runId: config.runId, + caseId, + phase, + capturedAt: new Date().toISOString(), + serverUrl: context.serverUrl, + }; + + if (!config.captureMemory) { + await writeJsonArtifact(targetPath, { + ...base, + ok: true, + status: 'disabled', + } satisfies MemorySnapshotArtifact); + return { path: targetPath, ok: true }; + } + + try { + const response = await fetch(`${context.serverUrl}/debug/memory`); + let payload: unknown; + const text = await response.text(); + try { + payload = text ? JSON.parse(text) : null; + } catch { + payload = text; + } + const ok = response.ok; + await writeJsonArtifact(targetPath, { + ...base, + ok, + status: ok ? 'captured' : 'unavailable', + httpStatus: response.status, + payload, + } satisfies MemorySnapshotArtifact); + return { path: targetPath, ok }; + } catch (error) { + await writeJsonArtifact(targetPath, { + ...base, + ok: false, + status: 'failed', + error: toRedactedErrorSample(error, { operation: 'memory.capture' }), + } satisfies MemorySnapshotArtifact); + return { path: targetPath, ok: false }; + } +}; diff --git a/graphql/server-test/perf/src/operations.ts b/graphql/server-test/perf/src/operations.ts new file mode 100644 index 0000000000..1a9d45dccf --- /dev/null +++ b/graphql/server-test/perf/src/operations.ts @@ -0,0 +1,421 @@ +import { toRedactedErrorSample } from './artifacts'; +import type { + BenchmarkContext, + MatrixCase, + PerfRunConfig, + RedactedErrorSample, + RequestOutcome, + RequestProfile, + RuntimeOperationProfile, +} from './types'; + +export const DEFAULT_DATABASE_ID = '028752cb-510b-1438-2f39-64534bd1cbd7'; +export const CONSTRUCTIVE_SCHEMAS = ['constructive_public']; +export const META_SCHEMAS = [ + 'services_public', + 'metaschema_public', + 'metaschema_modules_public', + 'constructive_auth_public', +]; +export const SERVICES_SCHEMATA = 'services_public,metaschema_public'; +export const MODULES_SCHEMATA = 'metaschema_modules_public'; +export const PRIVATE_API_NAME = 'private'; + +export interface GraphqlHttpResult { + status: number; + body: { + data?: T; + errors?: Array<{ message?: string; extensions?: Record }>; + }; + text?: string; +} + +export const executeGraphql = async ( + context: BenchmarkContext, + input: { + query: string; + variables?: Record; + headers?: Record; + surface?: 'public' | 'private'; + } +): Promise> => { + const request = input.surface === 'private' && context.privateRequest + ? context.privateRequest + : context.conn.request; + let req = request + .post('/graphql') + .set('Content-Type', 'application/json'); + + for (const [key, value] of Object.entries(input.headers || {})) { + req = req.set(key, value); + } + + const response = await req.send({ query: input.query, variables: input.variables }); + return { + status: response.status, + body: response.body || {}, + text: response.text, + }; +}; + +export const metaHeaders = (): Record => ({ + 'X-Database-Id': DEFAULT_DATABASE_ID, + 'X-Meta-Schema': 'true', +}); + +export const privateApiHeaders = (): Record => ({ + 'X-Database-Id': DEFAULT_DATABASE_ID, + 'X-Api-Name': PRIVATE_API_NAME, +}); + +export const privateServicesHeaders = (): Record => ({ + 'X-Database-Id': DEFAULT_DATABASE_ID, + 'X-Schemata': SERVICES_SCHEMATA, +}); + +export const privateModulesHeaders = (): Record => ({ + 'X-Database-Id': DEFAULT_DATABASE_ID, + 'X-Schemata': MODULES_SCHEMATA, +}); + +export const publicHostHeaders = (host: string): Record => ({ Host: host }); + +const stringMetadata = (requestProfile: RequestProfile, key: string): string | undefined => { + const value = requestProfile.metadata[key]; + return typeof value === 'string' && value ? value : undefined; +}; + +const requiredStringMetadata = (requestProfile: RequestProfile, key: string): string => { + const value = stringMetadata(requestProfile, key); + if (!value) { + throw new Error(`Request profile ${requestProfile.id} is missing ${key} metadata`); + } + return value; +}; + +const dataObject = (responseBody: unknown): Record | undefined => { + const body = responseBody as { data?: unknown } | undefined; + return body?.data && typeof body.data === 'object' ? body.data as Record : undefined; +}; + +const firstNodeId = (responseBody: unknown, fieldName: string): string | undefined => { + const value = dataObject(responseBody)?.[fieldName]?.nodes?.[0]?.id; + return typeof value === 'string' && value ? value : undefined; +}; + +const mutationNodeId = (responseBody: unknown, mutationField: string, nodeField: string): string | undefined => { + const value = dataObject(responseBody)?.[mutationField]?.[nodeField]?.id; + return typeof value === 'string' && value ? value : undefined; +}; + +const labelValue = (input: { + sequence: number; + requestProfile: RequestProfile; + config: PerfRunConfig; +}): string => `${input.config.benchmarkOwnedPrefix}-${input.requestProfile.id}-${input.sequence}`; + +const metadataQuery = `query PerfMetadataRead { + databases(first: 3) { nodes { id name } } + schemas(first: 5) { nodes { id name schemaName isPublic } } + apis(first: 5) { nodes { id name isPublic databaseId } } +}`; + +const modulesQuery = `query PerfModulesRead($first: Int!) { + databaseProvisionModules(first: $first) { nodes { id status databaseId completedAt } } +}`; + +const privateTypenameOperation = (workloadProfile: string, weight = 2): RuntimeOperationProfile => ({ + id: 'private-typename', + name: 'private.typename', + weight, + workloadProfile, + description: 'Tiny private schema sanity query.', + mutates: false, + query: '{ __typename }', +}); + +const buildMetadataReadPrivateOperationProfiles = (workloadProfile: string): RuntimeOperationProfile[] => [ + { + id: 'private-services-metadata', + name: 'private.services.metadata', + weight: 5, + workloadProfile, + description: 'Read Constructive services metadata through private direct schema routing.', + mutates: false, + compatibleRequestProfileIds: ['private-services', 'private-meta'], + query: metadataQuery, + }, + { + ...privateTypenameOperation(workloadProfile), + compatibleRequestProfileIds: ['private-services', 'private-modules', 'private-meta'], + }, + { + id: 'private-modules-dbpm-read', + name: 'private.modules.dbpmRead', + weight: 3, + workloadProfile, + description: 'Read DBPM module records through private modules schema routing.', + mutates: false, + compatibleRequestProfileIds: ['private-modules'], + query: modulesQuery, + variables: { first: 3 }, + }, +]; + +export const buildPrivateOperationProfiles = (workloadProfile: string): RuntimeOperationProfile[] => { + if (workloadProfile === 'cache-key-shared') { + return [ + { + id: 'private-services-metadata', + name: 'private.services.metadata', + weight: 8, + workloadProfile, + description: 'Read Constructive services metadata while varying svc_key only.', + mutates: false, + query: metadataQuery, + }, + privateTypenameOperation(workloadProfile, 2), + ]; + } + + if (workloadProfile === 'cache-key-distinct') { + return [privateTypenameOperation(workloadProfile, 10)]; + } + + return buildMetadataReadPrivateOperationProfiles(workloadProfile); +}; + +const buildPublicReadOnlyOperationProfiles = (workloadProfile: string): RuntimeOperationProfile[] => [ + { + id: 'public-business-list', + name: 'public.business.list', + weight: 8, + workloadProfile, + description: 'List benchmark-owned DBPM business table rows through public host routing.', + mutates: false, + query: '{ __typename }', + buildQuery: ({ requestProfile }) => { + const listField = requiredStringMetadata(requestProfile, 'listField'); + return `query PerfPublicBusinessList($first: Int!) { ${listField}(first: $first) { nodes { id label } } }`; + }, + variables: { first: 5 }, + extractRowId: ({ responseBody, requestProfile }) => + firstNodeId(responseBody, requiredStringMetadata(requestProfile, 'listField')), + }, + { + id: 'public-typename', + name: 'public.typename', + weight: 2, + workloadProfile, + description: 'Tiny public route sanity query.', + mutates: false, + query: '{ __typename }', + }, +]; + +const buildPublicCrudOperationProfiles = (workloadProfile: string): RuntimeOperationProfile[] => [ + { + id: 'public-business-create', + name: 'public.business.create', + weight: 2, + workloadProfile, + description: 'Create benchmark-owned DBPM business table rows through public host routing.', + mutates: true, + query: '{ __typename }', + buildQuery: ({ requestProfile }) => { + const createField = requiredStringMetadata(requestProfile, 'createField'); + const createInputType = requiredStringMetadata(requestProfile, 'createInputType'); + const nodeField = requiredStringMetadata(requestProfile, 'nodeField'); + return `mutation PerfPublicBusinessCreate($input: ${createInputType}!) { ${createField}(input: $input) { ${nodeField} { id label } } }`; + }, + buildVariables: ({ sequence, requestProfile, config }) => { + const nodeField = requiredStringMetadata(requestProfile, 'nodeField'); + return { + input: { + [nodeField]: { + label: labelValue({ sequence, requestProfile, config }), + }, + }, + }; + }, + producesRowId: true, + extractRowId: ({ responseBody, requestProfile }) => + mutationNodeId( + responseBody, + requiredStringMetadata(requestProfile, 'createField'), + requiredStringMetadata(requestProfile, 'nodeField') + ), + }, + { + id: 'public-business-get-by-id', + name: 'public.business.getById', + weight: 4, + workloadProfile, + description: 'Read a benchmark-owned DBPM business row by id through public host routing.', + mutates: false, + query: '{ __typename }', + buildQuery: ({ requestProfile }) => { + const listField = requiredStringMetadata(requestProfile, 'listField'); + return `query PerfPublicBusinessGetById($id: UUID!) { ${listField}(where: { id: { equalTo: $id } }, first: 1) { nodes { id label } } }`; + }, + buildVariables: ({ rowId }) => ({ id: rowId }), + requiresExistingRow: true, + extractRowId: ({ responseBody, requestProfile }) => + firstNodeId(responseBody, requiredStringMetadata(requestProfile, 'listField')), + }, + { + id: 'public-business-update-by-id', + name: 'public.business.updateById', + weight: 2, + workloadProfile, + description: 'Update a benchmark-owned DBPM business row by id through public host routing.', + mutates: true, + query: '{ __typename }', + buildQuery: ({ requestProfile }) => { + const updateField = requiredStringMetadata(requestProfile, 'updateField'); + const updateInputType = requiredStringMetadata(requestProfile, 'updateInputType'); + const nodeField = requiredStringMetadata(requestProfile, 'nodeField'); + return `mutation PerfPublicBusinessUpdateById($input: ${updateInputType}!) { ${updateField}(input: $input) { ${nodeField} { id label } } }`; + }, + buildVariables: ({ sequence, rowId, requestProfile, config }) => { + const patchField = requiredStringMetadata(requestProfile, 'patchField'); + return { + input: { + id: rowId, + [patchField]: { + label: labelValue({ sequence, requestProfile, config }), + }, + }, + }; + }, + requiresExistingRow: true, + producesRowId: true, + extractRowId: ({ responseBody, requestProfile }) => + mutationNodeId( + responseBody, + requiredStringMetadata(requestProfile, 'updateField'), + requiredStringMetadata(requestProfile, 'nodeField') + ), + }, + { + id: 'public-business-list-recent', + name: 'public.business.listRecent', + weight: 2, + workloadProfile, + description: 'List recent benchmark-owned DBPM business table rows through public host routing.', + mutates: false, + query: '{ __typename }', + buildQuery: ({ requestProfile }) => { + const listField = requiredStringMetadata(requestProfile, 'listField'); + return `query PerfPublicBusinessListRecent($first: Int!) { ${listField}(first: $first) { nodes { id label } } }`; + }, + variables: { first: 10 }, + extractRowId: ({ responseBody, requestProfile }) => + firstNodeId(responseBody, requiredStringMetadata(requestProfile, 'listField')), + }, + { + id: 'public-typename', + name: 'public.typename', + weight: 1, + workloadProfile, + description: 'Tiny public route sanity query.', + mutates: false, + query: '{ __typename }', + }, +]; + +export const buildPublicOperationProfiles = (workloadProfile: string): RuntimeOperationProfile[] => + workloadProfile === 'business-read' + ? buildPublicReadOnlyOperationProfiles(workloadProfile) + : buildPublicCrudOperationProfiles(workloadProfile); + +export const buildOperationBag = (operations: RuntimeOperationProfile[]): RuntimeOperationProfile[] => { + const bag: RuntimeOperationProfile[] = []; + for (const operation of operations) { + const weight = Math.max(1, Math.floor(operation.weight)); + for (let i = 0; i < weight; i += 1) bag.push(operation); + } + return bag; +}; + +export const compatibleOperationsFor = ( + requestProfile: RequestProfile, + operations: RuntimeOperationProfile[] +): RuntimeOperationProfile[] => + operations.filter( + (operation) => + !operation.compatibleRequestProfileIds || operation.compatibleRequestProfileIds.includes(requestProfile.id) + ); + +export const executeOperation = async (input: { + context: BenchmarkContext; + requestProfile: RequestProfile; + operation: RuntimeOperationProfile; + sequence: number; + rowId?: string; + config: PerfRunConfig; + matrixCase: MatrixCase; +}): Promise => { + const { context, requestProfile, operation, sequence, rowId, config, matrixCase } = input; + const started = performance.now(); + try { + const variables = operation.buildVariables + ? operation.buildVariables({ sequence, rowId, requestProfile, config, matrixCase }) + : operation.variables; + const query = operation.buildQuery + ? operation.buildQuery({ sequence, rowId, requestProfile, config, matrixCase }) + : operation.query; + const response = await executeGraphql(context, { + query, + variables, + headers: requestProfile.headers, + }); + const latencyMs = performance.now() - started; + const graphqlErrors = response.body?.errors?.length ?? 0; + const ok = response.status >= 200 && response.status < 300 && graphqlErrors === 0; + const error = ok + ? undefined + : toRedactedErrorSample(response.body?.errors || response.text || `HTTP ${response.status}`, { + operation: operation.name, + requestProfileId: requestProfile.id, + status: response.status, + }) as RedactedErrorSample; + + return { + ok, + latencyMs, + operation: operation.name, + requestProfileId: requestProfile.id, + status: response.status, + unexpectedGraphqlErrors: graphqlErrors, + rowId: ok && operation.extractRowId + ? operation.extractRowId({ responseBody: response.body, sequence, rowId, requestProfile, config, matrixCase }) + : undefined, + error, + }; + } catch (error) { + return { + ok: false, + latencyMs: performance.now() - started, + operation: operation.name, + requestProfileId: requestProfile.id, + unexpectedGraphqlErrors: 0, + error: toRedactedErrorSample(error, { + operation: operation.name, + requestProfileId: requestProfile.id, + }) as RedactedErrorSample, + }; + } +}; + +export const responseHasUnexpectedErrors = (response: GraphqlHttpResult): boolean => + response.status < 200 || response.status >= 300 || Boolean(response.body?.errors?.length); + +export const responseErrorSample = ( + response: GraphqlHttpResult, + input: { operation?: string; requestProfileId?: string } +): RedactedErrorSample => + toRedactedErrorSample(response.body?.errors || response.text || `HTTP ${response.status}`, { + ...input, + status: response.status, + }) as RedactedErrorSample; diff --git a/graphql/server-test/perf/src/preflight.ts b/graphql/server-test/perf/src/preflight.ts new file mode 100644 index 0000000000..ce4cae2950 --- /dev/null +++ b/graphql/server-test/perf/src/preflight.ts @@ -0,0 +1,193 @@ +import { preflightReportPath, writeJsonArtifact } from './artifacts'; +import { provisionDbpmPublic } from './dbpm-provision'; +import { compatibleOperationsFor, executeOperation } from './operations'; +import { buildPublicProfilesFromProvision } from './profiles'; +import { preparePublicBusinessWorkload } from './setup'; +import type { + BenchmarkContext, + MatrixCase, + PerfRunConfig, + PreflightReport, + PublicPreflightResult, + RedactedErrorSample, + RequestProfile, + RouteProbeSummary, + RunArtifactPaths, + RuntimeOperationProfile, +} from './types'; +import { PERF_SCHEMA_VERSION } from './types'; + +export const probeProfiles = async (input: { + context: BenchmarkContext; + matrixCase: MatrixCase; + config: PerfRunConfig; + requestProfiles: RequestProfile[]; + operationProfiles: RuntimeOperationProfile[]; +}): Promise => { + const { context, matrixCase, config, operationProfiles } = input; + const sampleSize = config.routeProbeSampleSize; + const profiles = sampleSize === 0 + ? input.requestProfiles + : input.requestProfiles.slice(0, Math.max(1, sampleSize)); + + const errors: RedactedErrorSample[] = []; + let succeeded = 0; + let unexpectedGraphqlErrors = 0; + + for (const [index, requestProfile] of profiles.entries()) { + const compatible = compatibleOperationsFor(requestProfile, operationProfiles); + const operation = + compatible.find((candidate) => !candidate.mutates && !candidate.requiresExistingRow) || + compatible.find((candidate) => !candidate.mutates) || + compatible[0]; + if (!operation) { + errors.push({ + at: new Date().toISOString(), + operation: 'routeProbe.selectOperation', + requestProfileId: requestProfile.id, + message: `No compatible operation for request profile ${requestProfile.id}`, + }); + continue; + } + const outcome = await executeOperation({ + context, + requestProfile, + operation, + sequence: index, + config, + matrixCase, + }); + unexpectedGraphqlErrors += outcome.unexpectedGraphqlErrors; + if (outcome.ok) succeeded += 1; + else if (outcome.error && errors.length < config.errorSampleLimit) errors.push(outcome.error); + } + + const attempted = profiles.length; + const failed = attempted - succeeded; + return { + ok: attempted > 0 && failed === 0 && unexpectedGraphqlErrors === 0, + attempted, + succeeded, + failed, + unexpectedGraphqlErrors, + errorSamples: errors, + }; +}; + +export const runPublicPreflight = async (input: { + context: BenchmarkContext; + matrixCase: MatrixCase; + config: PerfRunConfig; + artifacts: RunArtifactPaths; +}): Promise => { + const { context, matrixCase, config, artifacts } = input; + const startedAt = new Date().toISOString(); + const artifactPath = preflightReportPath(artifacts, matrixCase.caseId); + const hardGateFailures: string[] = []; + + const provision = await provisionDbpmPublic({ context, matrixCase, config }); + const generated = buildPublicProfilesFromProvision(provision, matrixCase); + const setup = await preparePublicBusinessWorkload({ + context, + matrixCase, + config, + requestProfiles: generated.requestProfiles, + }); + const requestProfiles = setup.requestProfiles; + const operationProfiles = generated.operationProfiles; + + const routeProbe = await probeProfiles({ + context, + matrixCase, + config, + requestProfiles, + operationProfiles, + }); + + if (!provision.completed || !provision.ok) hardGateFailures.push('DBPM provision did not complete without unexpected errors'); + if ( + requestProfiles.length < matrixCase.scaleProfile.k && + !config.publicPreflight.allowUnderProvisioned + ) { + hardGateFailures.push( + `provisioned public profile count ${requestProfiles.length} is below k=${matrixCase.scaleProfile.k}` + ); + } + if ( + provision.businessTableCount < matrixCase.scaleProfile.k && + !config.publicPreflight.allowUnderProvisioned + ) { + hardGateFailures.push( + `provisioned business table count ${provision.businessTableCount} is below k=${matrixCase.scaleProfile.k}` + ); + } + if (requestProfiles.length === 0) hardGateFailures.push('request profiles are empty'); + if (operationProfiles.length === 0) hardGateFailures.push('operation profiles are empty'); + if (setup.touchedNonBenchmarkObjects) hardGateFailures.push('access/setup touched non-benchmark objects'); + if (!setup.ok) hardGateFailures.push('business workload preparation failed'); + if (!routeProbe.ok) hardGateFailures.push('route probes failed'); + if (routeProbe.unexpectedGraphqlErrors !== 0) hardGateFailures.push('route probes returned unexpected GraphQL errors'); + + const report: PreflightReport = { + schemaVersion: PERF_SCHEMA_VERSION, + runId: config.runId, + caseId: matrixCase.caseId, + startedAt, + finishedAt: new Date().toISOString(), + ok: hardGateFailures.length === 0, + configSnapshot: { + routingMode: matrixCase.routingMode, + cacheMode: matrixCase.cacheMode, + scaleProfile: matrixCase.scaleProfile, + workloadProfile: matrixCase.workloadProfile, + publicPreflight: config.publicPreflight, + serverUrl: context.serverUrl, + contextId: context.id, + provisionWarnings: provision.warnings, + benchmarkOwnedPrefix: config.benchmarkOwnedPrefix, + cacheSizes: config.cacheSizes, + effectiveCacheEnv: config.effectiveCacheEnv, + }, + provision: { + ok: provision.ok, + tenantCount: provision.tenantCount, + publicHostCount: provision.publicHostCount, + apiCount: provision.apiCount, + businessTableCount: provision.businessTableCount, + errors: [...provision.errors, ...setup.errors], + source: provision.source, + warnings: provision.warnings, + }, + profiles: { + requestProfileCount: requestProfiles.length, + operationProfileCount: operationProfiles.length, + routeKeyCount: new Set(requestProfiles.map((profile) => profile.routeKey)).size, + }, + routeProbe, + hardGateFailures, + }; + + try { + await writeJsonArtifact(artifactPath, report); + } catch (error) { + hardGateFailures.push(`preflight artifact write failed: ${error instanceof Error ? error.message : String(error)}`); + } + + return { + ok: hardGateFailures.length === 0, + routingMode: 'public', + scaleProfile: matrixCase.scaleProfile.name, + workloadProfile: matrixCase.workloadProfile, + k: matrixCase.scaleProfile.k, + provisionedTenantCount: provision.tenantCount, + publicHostCount: provision.publicHostCount, + requestProfileCount: requestProfiles.length, + operationProfileCount: operationProfiles.length, + routeProbe, + requestProfiles, + operationProfiles, + artifactPath, + hardGateFailures, + errorSamples: [...provision.errors, ...setup.errors, ...routeProbe.errorSamples], + }; +}; diff --git a/graphql/server-test/perf/src/profiles.ts b/graphql/server-test/perf/src/profiles.ts new file mode 100644 index 0000000000..227b47e4e7 --- /dev/null +++ b/graphql/server-test/perf/src/profiles.ts @@ -0,0 +1,168 @@ +import { + buildPrivateOperationProfiles, + buildPublicOperationProfiles, + DEFAULT_DATABASE_ID, + META_SCHEMAS, + MODULES_SCHEMATA, + SERVICES_SCHEMATA, + metaHeaders, + privateModulesHeaders, + privateServicesHeaders, +} from './operations'; +import type { + DbpmProvisionResult, + MatrixCase, + PerfRunConfig, + RequestProfile, + RuntimeOperationProfile, +} from './types'; + +const privateSchemataHeaders = (databaseId: string, schemata: string): Record => ({ + 'X-Database-Id': databaseId, + 'X-Schemata': schemata, +}); + +const basePrivateProfiles = (): RequestProfile[] => [ + { + id: 'private-services', + routingMode: 'private', + routeKey: `schemata:${DEFAULT_DATABASE_ID}:${SERVICES_SCHEMATA}`, + headers: privateServicesHeaders(), + description: 'Constructive-local services/metaschema route selected by X-Schemata.', + benchmarkOwned: false, + metadata: { source: 'constructive-local' }, + }, + { + id: 'private-modules', + routingMode: 'private', + routeKey: `schemata:${DEFAULT_DATABASE_ID}:${MODULES_SCHEMATA}`, + headers: privateModulesHeaders(), + description: 'Constructive-local DBPM modules route selected by X-Schemata.', + benchmarkOwned: false, + metadata: { source: 'constructive-local' }, + }, + { + id: 'private-meta', + routingMode: 'private', + routeKey: `metaschema:api:${DEFAULT_DATABASE_ID}`, + headers: metaHeaders(), + description: 'Private metadata route selected by X-Meta-Schema.', + benchmarkOwned: false, + metadata: { source: 'constructive-local' }, + }, +]; + +const syntheticDatabaseId = (config: PerfRunConfig, index: number): string => + `${config.benchmarkOwnedPrefix}-db-${String(index + 1).padStart(4, '0')}`; + +const schemaCombinations = (): string[] => { + const sets: string[] = []; + for (let mask = 1; mask < (1 << META_SCHEMAS.length); mask += 1) { + const schemas = META_SCHEMAS.filter((_, index) => (mask & (1 << index)) !== 0); + sets.push(schemas.join(',')); + } + return sets; +}; + +const buildSharedBuildKeyProfiles = (matrixCase: MatrixCase, config: PerfRunConfig): RequestProfile[] => { + const target = Math.max(1, matrixCase.scaleProfile.k); + return Array.from({ length: target }, (_, index) => { + const databaseId = syntheticDatabaseId(config, index); + return { + id: `private-cache-shared-${index + 1}`, + routingMode: 'private', + routeKey: `schemata:${databaseId}:${SERVICES_SCHEMATA}`, + headers: privateSchemataHeaders(databaseId, SERVICES_SCHEMATA), + description: 'Private route with a distinct svc_key and shared Graphile build inputs.', + benchmarkOwned: true, + metadata: { + source: 'constructive-local-synthetic', + cacheKeyMode: 'shared-build-key', + databaseId, + schemata: SERVICES_SCHEMATA, + expectedBuildKeyGroup: SERVICES_SCHEMATA, + }, + }; + }); +}; + +const buildDistinctBuildKeyProfiles = (matrixCase: MatrixCase, config: PerfRunConfig): RequestProfile[] => { + const target = Math.max(1, matrixCase.scaleProfile.k); + const schemaSets = schemaCombinations(); + return Array.from({ length: target }, (_, index) => { + const databaseId = syntheticDatabaseId(config, index); + const schemata = schemaSets[index % schemaSets.length]; + return { + id: `private-cache-distinct-${index + 1}`, + routingMode: 'private', + routeKey: `schemata:${databaseId}:${schemata}`, + headers: privateSchemataHeaders(databaseId, schemata), + description: 'Private route with distinct svc_key and distinct Graphile build inputs.', + benchmarkOwned: true, + metadata: { + source: 'constructive-local-synthetic', + cacheKeyMode: 'distinct-build-key', + databaseId, + schemata, + expectedBuildKeyGroup: schemata, + repeatedBuildKey: index >= schemaSets.length, + }, + }; + }); +}; + +const buildMetadataReadProfiles = ( + matrixCase: MatrixCase, + config: PerfRunConfig +): RequestProfile[] => { + const bases = basePrivateProfiles(); + const requestProfiles: RequestProfile[] = []; + const target = Math.max(1, matrixCase.scaleProfile.k); + for (let i = 0; i < target; i += 1) { + const base = bases[i % bases.length]; + requestProfiles.push( + i < bases.length + ? base + : { + ...base, + id: `${base.id}-${i + 1}`, + routeKey: `${base.routeKey}#profile-${i + 1}`, + headers: { ...base.headers, 'X-Perf-Profile': `${config.benchmarkOwnedPrefix}-${i + 1}` }, + metadata: { ...base.metadata, repeatedRoute: true, baseProfileId: base.id }, + } + ); + } + return requestProfiles; +}; + +export const buildPrivateProfiles = ( + matrixCase: MatrixCase, + config: PerfRunConfig +): { requestProfiles: RequestProfile[]; operationProfiles: RuntimeOperationProfile[] } => { + const requestProfiles = matrixCase.workloadProfile === 'cache-key-shared' + ? buildSharedBuildKeyProfiles(matrixCase, config) + : matrixCase.workloadProfile === 'cache-key-distinct' + ? buildDistinctBuildKeyProfiles(matrixCase, config) + : buildMetadataReadProfiles(matrixCase, config); + + const operations = buildPrivateOperationProfiles(matrixCase.workloadProfile).map((operation) => { + if (!operation.compatibleRequestProfileIds) return operation; + const expandedIds = requestProfiles + .filter((profile) => { + const baseId = String(profile.metadata.baseProfileId || profile.id); + return operation.compatibleRequestProfileIds!.includes(baseId); + }) + .map((profile) => profile.id); + return { ...operation, compatibleRequestProfileIds: expandedIds }; + }); + + return { requestProfiles, operationProfiles: operations }; +}; + +export const buildPublicProfilesFromProvision = ( + provision: Pick, + matrixCase: MatrixCase +): { requestProfiles: RequestProfile[]; operationProfiles: RuntimeOperationProfile[] } => ({ + requestProfiles: provision.requestProfiles, + operationProfiles: buildPublicOperationProfiles(matrixCase.workloadProfile), +}); diff --git a/graphql/server-test/perf/src/reset.ts b/graphql/server-test/perf/src/reset.ts new file mode 100644 index 0000000000..9ac1124d87 --- /dev/null +++ b/graphql/server-test/perf/src/reset.ts @@ -0,0 +1,9 @@ +import type { BenchmarkContext, MatrixCase, PerfRunConfig } from './types'; + +export const resetBenchmarkOwnedState = async (_input: { + context: BenchmarkContext; + matrixCase: MatrixCase; + config: PerfRunConfig; +}): Promise<{ ok: boolean; hardGateFailures: string[] }> => { + return { ok: true, hardGateFailures: [] }; +}; diff --git a/graphql/server-test/perf/src/setup.ts b/graphql/server-test/perf/src/setup.ts new file mode 100644 index 0000000000..f87a860975 --- /dev/null +++ b/graphql/server-test/perf/src/setup.ts @@ -0,0 +1,37 @@ +import { toRedactedErrorSample } from './artifacts'; +import type { + BenchmarkContext, + MatrixCase, + PerfRunConfig, + RedactedErrorSample, + RequestProfile, +} from './types'; + +export const preparePublicBusinessWorkload = async (input: { + context: BenchmarkContext; + matrixCase: MatrixCase; + config: PerfRunConfig; + requestProfiles: RequestProfile[]; +}): Promise<{ + ok: boolean; + requestProfiles: RequestProfile[]; + touchedNonBenchmarkObjects: boolean; + errors: RedactedErrorSample[]; +}> => { + const errors: RedactedErrorSample[] = []; + for (const profile of input.requestProfiles) { + if (!profile.benchmarkOwned || profile.metadata.source !== 'constructive-local-dbpm-graphql') { + errors.push(toRedactedErrorSample(`Public workload profile is not benchmark-owned: ${profile.id}`, { + operation: 'public.setup.scopeGuard', + requestProfileId: profile.id, + }) as RedactedErrorSample); + } + } + + return { + ok: errors.length === 0, + requestProfiles: input.requestProfiles, + touchedNonBenchmarkObjects: errors.length > 0, + errors, + }; +}; diff --git a/graphql/server-test/perf/src/stats.ts b/graphql/server-test/perf/src/stats.ts new file mode 100644 index 0000000000..9b0f764cd2 --- /dev/null +++ b/graphql/server-test/perf/src/stats.ts @@ -0,0 +1,60 @@ +import type { LoadReport, RequestOutcome } from './types'; + +const percentile = (sorted: number[], p: number): number | null => { + if (sorted.length === 0) return null; + const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1)); + return sorted[index]; +}; + +const maxOf = (values: number[]): number | null => { + if (values.length === 0) return null; + let max = values[0]; + for (let i = 1; i < values.length; i += 1) { + if (values[i] > max) max = values[i]; + } + return max; +}; + +export const summarizeOutcomes = (input: { + outcomes: RequestOutcome[]; + durationMs: number; + durationSeconds: number; + workers: number; + failFast?: LoadReport['failFast']; + errorSampleLimit: number; +}): LoadReport => { + const { outcomes, durationMs, durationSeconds, workers, failFast, errorSampleLimit } = input; + const latencies = outcomes.map((outcome) => outcome.latencyMs).sort((a, b) => a - b); + const failedRequests = outcomes.filter((outcome) => !outcome.ok).length; + const unexpectedGraphqlErrors = outcomes.reduce((sum, outcome) => sum + outcome.unexpectedGraphqlErrors, 0); + const operations: LoadReport['operations'] = {}; + + for (const outcome of outcomes) { + operations[outcome.operation] ||= { total: 0, failed: 0 }; + operations[outcome.operation].total += 1; + if (!outcome.ok) operations[outcome.operation].failed += 1; + } + + return { + ok: failedRequests === 0 && unexpectedGraphqlErrors === 0 && !failFast?.triggered, + durationSeconds, + workers, + totalRequests: outcomes.length, + failedRequests, + unexpectedGraphqlErrors, + qps: durationMs > 0 ? outcomes.length / (durationMs / 1000) : 0, + latencyMs: { + p50: percentile(latencies, 50), + p90: percentile(latencies, 90), + p95: percentile(latencies, 95), + p99: percentile(latencies, 99), + max: maxOf(latencies), + }, + operations, + ...(failFast && { failFast }), + errorSamples: outcomes + .map((outcome) => outcome.error) + .filter((sample): sample is NonNullable => Boolean(sample)) + .slice(0, errorSampleLimit), + }; +}; diff --git a/graphql/server-test/perf/src/types.ts b/graphql/server-test/perf/src/types.ts new file mode 100644 index 0000000000..2b196edc70 --- /dev/null +++ b/graphql/server-test/perf/src/types.ts @@ -0,0 +1,392 @@ +import type supertest from 'supertest'; + +import type { GetConnectionsResult, ServerInfo } from '../../src/types'; + +export const PERF_SCHEMA_VERSION = 1 as const; + +export type SchemaVersion = typeof PERF_SCHEMA_VERSION; +export type RoutingMode = 'private' | 'public'; +export type CacheMode = 'old' | 'new'; +export type ConnectionPolicy = 'reuse' | 'per-case'; +export type CaseStatus = 'passed' | 'failed' | 'skipped'; +export type MemoryPhase = 'before' | 'after'; + +export interface ScaleProfile { + name: string; + k: number; + durationSeconds: number; + workers: number; +} + +export interface WorkloadProfileSelection { + private: string; + public: string; +} + +export interface PublicPreflightConfig { + allowUnderProvisioned: boolean; +} + +export interface CacheSizeConfig { + graphileCacheMax?: number; + multiTenancyCacheMax?: number; + pgCacheMax?: number; +} + +export interface PerfConfigSource { + defaults: string; + group: string; + configPath?: string; + envOverrides: Record; +} + +export interface PerfRunConfig { + schemaVersion: SchemaVersion; + runId: string; + name: string; + configGroup: string; + configPath?: string; + runDir: string; + connectionPolicy: ConnectionPolicy; + routingModes: RoutingMode[]; + cacheModes: CacheMode[]; + scaleProfile: ScaleProfile; + workloadProfiles: WorkloadProfileSelection; + publicPreflight: PublicPreflightConfig; + cacheSizes: CacheSizeConfig; + effectiveCacheEnv: Record; + failFast: boolean; + captureMemory: boolean; + routeProbeSampleSize: number; + errorSampleLimit: number; + testTimeoutMs: number; + constructiveLocalPath: string; + benchmarkOwnedPrefix: string; + source: PerfConfigSource; +} + +export type PerfRunConfigOverlay = Partial< + Omit< + PerfRunConfig, + | 'schemaVersion' + | 'runId' + | 'source' + | 'benchmarkOwnedPrefix' + | 'scaleProfile' + | 'workloadProfiles' + | 'publicPreflight' + | 'constructiveLocalPath' + > +> & { + scaleProfile?: Partial & { name?: string }; + workloadProfiles?: Partial; + publicPreflight?: Partial; + cacheSizes?: Partial; + constructiveLocalPath?: string; +}; + +export interface MatrixCase { + caseId: string; + routingMode: RoutingMode; + cacheMode: CacheMode; + scaleProfile: ScaleProfile; + workloadProfile: string; + mutates: boolean; +} + +export interface BenchmarkContext { + id: string; + compatibilityKey: string; + reused: boolean; + createdAt: string; + serverUrl: string; + graphqlUrl: string; + conn: GetConnectionsResult; + privateServer?: ServerInfo; + privateRequest?: supertest.Agent; + privateServerUrl?: string; +} + +export interface RequestProfile { + id: string; + routingMode: RoutingMode; + routeKey: string; + headers: Record; + description: string; + benchmarkOwned: boolean; + metadata: Record; +} + +export interface OperationProfile { + id: string; + name: string; + weight: number; + workloadProfile: string; + description: string; + mutates: boolean; + compatibleRequestProfileIds?: string[]; + query: string; + variables?: Record; +} + +export interface RuntimeOperationProfile extends OperationProfile { + buildQuery?: (input: { + sequence: number; + rowId?: string; + requestProfile: RequestProfile; + config: PerfRunConfig; + matrixCase: MatrixCase; + }) => string; + buildVariables?: (input: { + sequence: number; + rowId?: string; + requestProfile: RequestProfile; + config: PerfRunConfig; + matrixCase: MatrixCase; + }) => Record | undefined; + extractRowId?: (input: { + responseBody: unknown; + sequence: number; + rowId?: string; + requestProfile: RequestProfile; + config: PerfRunConfig; + matrixCase: MatrixCase; + }) => string | undefined; + requiresExistingRow?: boolean; + producesRowId?: boolean; +} + +export interface RedactedErrorSample { + at: string; + operation?: string; + requestProfileId?: string; + message: string; + status?: number; + code?: string; + details?: unknown; +} + +export interface RouteProbeSummary { + ok: boolean; + attempted: number; + succeeded: number; + failed: number; + unexpectedGraphqlErrors: number; + errorSamples: RedactedErrorSample[]; +} + +export interface DbpmProvisionResult { + ok: boolean; + completed: boolean; + source: 'graphql' | 'unavailable'; + tenantCount: number; + publicHostCount: number; + apiCount: number; + businessTableCount: number; + requestProfiles: RequestProfile[]; + operationProfiles: RuntimeOperationProfile[]; + benchmarkOwnedObjectIds: string[]; + errors: RedactedErrorSample[]; + warnings: string[]; +} + +export interface PublicPreflightResult { + ok: boolean; + routingMode: 'public'; + scaleProfile: string; + workloadProfile: string; + k: number; + provisionedTenantCount: number; + publicHostCount: number; + requestProfileCount: number; + operationProfileCount: number; + routeProbe: RouteProbeSummary; + requestProfiles: RequestProfile[]; + operationProfiles: RuntimeOperationProfile[]; + artifactPath: string; + hardGateFailures: string[]; + errorSamples: RedactedErrorSample[]; +} + +export interface PreflightReport { + schemaVersion: SchemaVersion; + runId: string; + caseId: string; + startedAt: string; + finishedAt: string; + ok: boolean; + configSnapshot: Record; + provision: { + ok: boolean; + tenantCount: number; + publicHostCount: number; + apiCount: number; + businessTableCount: number; + reportPath?: string; + errors: RedactedErrorSample[]; + source: DbpmProvisionResult['source']; + warnings: string[]; + }; + profiles: { + requestProfileCount: number; + operationProfileCount: number; + routeKeyCount: number; + }; + routeProbe: RouteProbeSummary; + hardGateFailures: string[]; +} + +export interface RequestOutcome { + ok: boolean; + latencyMs: number; + operation: string; + requestProfileId: string; + status?: number; + unexpectedGraphqlErrors: number; + rowId?: string; + error?: RedactedErrorSample; +} + +export interface LoadReport { + ok: boolean; + durationSeconds: number; + workers: number; + totalRequests: number; + failedRequests: number; + unexpectedGraphqlErrors: number; + qps: number; + latencyMs: { + p50: number | null; + p90: number | null; + p95: number | null; + p99: number | null; + max: number | null; + }; + operations: Record; + failFast?: { + triggered: boolean; + reason?: string; + }; + errorSamples: RedactedErrorSample[]; +} + +export interface MemorySnapshotArtifact { + schemaVersion: SchemaVersion; + runId: string; + caseId: string; + phase: MemoryPhase; + capturedAt: string; + ok: boolean; + status: 'captured' | 'disabled' | 'unavailable' | 'failed'; + serverUrl: string; + httpStatus?: number; + payload?: unknown; + error?: RedactedErrorSample; +} + +export interface MemoryCaseArtifacts { + beforePath?: string; + afterPath?: string; + beforeOk: boolean; + afterOk: boolean; +} + +export interface CaseReport { + schemaVersion: SchemaVersion; + runId: string; + caseId: string; + startedAt: string; + finishedAt: string; + ok: boolean; + status: CaseStatus; + matrix: { + routingMode: RoutingMode; + cacheMode: CacheMode; + scaleProfile: ScaleProfile; + workloadProfile: string; + }; + lifecycle: { + connectionPolicy: ConnectionPolicy; + contextId: string; + contextReused: boolean; + compatibilityKey: string; + serverUrl: string; + }; + preflight?: { + ok: boolean; + artifactPath: string; + provisionedTenantCount?: number; + publicHostCount?: number; + requestProfileCount: number; + operationProfileCount: number; + hardGateFailures: string[]; + }; + routeProbe: RouteProbeSummary; + load?: LoadReport; + memory?: MemoryCaseArtifacts; + gates: { + hardGateFailures: string[]; + observations: Record; + }; + artifacts: { + caseReportPath: string; + preflightPath?: string; + memoryBeforePath?: string; + memoryAfterPath?: string; + errorsPath?: string; + }; +} + +export interface CaseSummary { + caseId: string; + status: CaseStatus; + ok: boolean; + routingMode: RoutingMode; + cacheMode: CacheMode; + workloadProfile: string; + contextId?: string; + totalRequests?: number; + failedRequests?: number; + unexpectedGraphqlErrors?: number; + qps?: number; + hardGateFailures: string[]; + caseReportPath: string; +} + +export interface RunArtifactPaths { + runDir: string; + summaryPath: string; + casesDir: string; + preflightDir: string; + memoryDir: string; + errorsDir: string; +} + +export interface PerfRunSummary { + schemaVersion: SchemaVersion; + runId: string; + runDir: string; + configGroup: string; + configPath?: string; + startedAt: string; + finishedAt: string; + pass: boolean; + config: PerfRunConfig; + totals: { + caseCount: number; + passed: number; + failed: number; + skipped: number; + }; + cases: CaseSummary[]; + artifacts: { + summaryPath: string; + casesDir: string; + preflightDir: string; + memoryDir: string; + errorsDir: string; + }; +} diff --git a/graphql/server/package.json b/graphql/server/package.json index 02d7c86ecb..83fd0e7dce 100644 --- a/graphql/server/package.json +++ b/graphql/server/package.json @@ -28,6 +28,8 @@ "dev:watch": "nodemon --watch src --ext ts --exec ts-node src/run.ts", "debug:memory:analyze": "node scripts/analyze-debug-logs.mjs", "debug:heap:capture": "node scripts/capture-heap-snapshot.mjs", + "perf": "ts-node perf/src/cli.ts", + "perf:typecheck": "tsc -p perf/tsconfig.json --noEmit", "lint": "eslint . --fix", "test": "jest --passWithNoTests", "test:watch": "jest --watch", @@ -63,6 +65,7 @@ "graphile-build-pg": "5.0.2", "graphile-cache": "workspace:^", "graphile-config": "1.0.1", + "graphile-multi-tenancy-cache": "workspace:^", "graphile-settings": "workspace:^", "graphile-utils": "5.0.1", "graphql": "16.13.0", diff --git a/graphql/server/perf/README.md b/graphql/server/perf/README.md new file mode 100644 index 0000000000..77e51ad845 --- /dev/null +++ b/graphql/server/perf/README.md @@ -0,0 +1,531 @@ +# Constructive GraphQL Server Perf Suite + +This directory is the home for GraphQL server performance tooling around the multi-tenancy cache work. + +This README documents the TypeScript perf CLI surface. Public DBPM and wrapper flows run through `perf/src/cli.ts`; private comparison and stress commands still delegate to the existing lightweight harnesses while their internals are migrated. + +## What This Tests + +The current optimization model is exact-match buildKey handler reuse: + +- no template sharing +- no SQL rewrite +- no fingerprint-based handler reuse in the runtime path +- handler reuse only when the effective build inputs match exactly + +The build inputs that matter for the current cache model are: + +- connection identity +- schema set +- `anonRole` +- `roleName` + +The benchmark goal is to compare the old dedicated-instance behavior against the new shared-handler behavior, and to verify that realistic multitenant public routing remains correct under load. + +## Supported Use Cases + +The maintained use cases are intentionally small. + +### 1. Lightweight private-routing comparison + +- routing mode: private/header routing +- server env: `API_IS_PUBLIC=false` +- data setup: synthetic route profiles, no DBPM tenant provisioning +- main purpose: quick old/new cache comparison +- commands: + - `pnpm --dir graphql/server perf private-benchmark` + - `pnpm --dir graphql/server perf private-compare` + +### 2. DBPM-backed public multitenant load + +- routing mode: public host routing +- server env: `API_IS_PUBLIC=true` +- data setup: real DBPM-provisioned tenants and business tables +- required local hosts: + - `auth.localhost` for `signUp` / `signIn` + - `modules.localhost` for DBPM provisioning mutations + - `api-dbpm-*.localhost` for provisioned business-table GraphQL +- main purpose: realistic tenant/business workload +- commands: + - `pnpm --dir graphql/server perf public-preflight` + - `pnpm --dir graphql/server perf public-load` + +### Wrapper flows + +These are orchestration wrappers around the two primary use cases. They are not separate perf models. + +- `pnpm --dir graphql/server perf run` +- `pnpm --dir graphql/server perf sweep` +- `pnpm --dir graphql/server perf stress` +- `pnpm --dir graphql/server perf e2e-matrix` + +Options such as shape variants, sweeps, stress matrices, prewarm, and longer duration runs are modifiers of the two maintained flows above. + +## TypeScript CLI Surface + +The CLI is the stable operator interface for perf runs. Some implementations still delegate to compatibility modules or existing private harnesses, but callers should use the `perf` command surface. + +Command surface: + +| Command | Purpose | Backing implementation today | +|---|---|---| +| `perf private-benchmark` | Run a lightweight private/header-routing HTTP benchmark | `perf/e2e-benchmark.ts` | +| `perf private-compare` | Run old/new private-routing comparison | `perf/run-comparison.sh` | +| `perf stress` | Run the curated private comparison stress matrix | `perf/run-stress-suite.sh` | +| `perf public-preflight` | Provision/validate DBPM tenants and generate profiles | `perf/src/legacy/phase1-preflight.ts` | +| `perf public-load` | Run DBPM-backed business load | `perf/src/legacy/phase2-load.ts` | +| `perf run` | Run preflight + load for one run directory | `perf/src/commands/run.ts` | +| `perf sweep` | Run repeated K/tenant-count sweeps | `perf/src/commands/sweep.ts` | +| `perf e2e-matrix` | Run public/private x old/new verification matrix | `perf/src/commands/e2e-matrix.ts` | +| `perf summarize-shapes` | Summarize DBPM shape variants | `perf/src/commands/summarize-shapes.ts` | +| `perf prepare-public-access` | Prepare public grants for perf business tables | `perf/src/commands/prepare-public-access.ts` | +| `perf reset-business-data` | Truncate generated business workload table data | `perf/src/commands/reset-business-data.ts` | + +Target library boundaries: + +```text +perf/src/cli.ts +perf/src/commands.ts +perf/src/commands/* +perf/src/lib/args.ts +perf/src/lib/config.ts +perf/src/lib/run-dir.ts +perf/src/lib/http.ts +perf/src/lib/graphql.ts +perf/src/lib/pg.ts +perf/src/lib/process.ts +perf/src/lib/stats.ts +perf/src/lib/reports.ts +perf/src/lib/profiles/* +perf/src/lib/dbpm/* +perf/src/lib/public-access.ts +perf/src/utils/* +perf/src/types.ts +``` + +The CLI keeps the public/private lane split explicit. It does not expose every helper as an equally important top-level concept. + +## Prerequisites + +Run commands from the repository root unless a command explicitly says otherwise. + +Use an already deployed Constructive database. This perf directory does not currently create and deploy a database from zero. + +Default PostgreSQL environment: + +```bash +export PGHOST=localhost +export PGPORT=5432 +export PGUSER=postgres +export PGPASSWORD= +export PGDATABASE=constructive +``` + +`postgres_perf` appears in older local notes only. It is not the current quick-start default. If you want an isolated perf database, create and deploy it externally, then set `PGDATABASE=postgres_perf` before running the perf commands. + +For local host routing, always avoid proxying localhost traffic: + +```bash +export NO_PROXY=localhost,127.0.0.1,::1 +export no_proxy=localhost,127.0.0.1,::1 +``` + +Use `/debug/memory` to verify server readiness and inspect cache state: + +```bash +curl http://localhost:3000/debug/memory +``` + +For the new multi-tenancy cache path, useful debug fields include `multiTenancyCache.handlerCacheSize` and `graphileBuilds.started` when present. + +## Perf Lanes + +| Lane | Routing | Data | Main use | Entrypoints | Backing implementation today | +|---|---|---|---|---|---| +| Lightweight HTTP comparison | private header routing, `API_IS_PUBLIC=false` | synthetic route profiles | old/new cache comparison | `perf private-benchmark`, `perf private-compare`, `perf stress` | `e2e-benchmark.ts`, `run-comparison.sh`, `run-stress-suite.sh` | +| DBPM-backed multitenant | public host routing, `API_IS_PUBLIC=true` | real provisioned tenants | realistic tenant/business load | `perf public-preflight`, `perf public-load`, `perf run`, `perf sweep` | `perf/src/legacy/phase1-preflight.ts`, `perf/src/legacy/phase2-load.ts`, `perf/src/commands/run.ts`, `perf/src/commands/sweep.ts` | + +Do not treat every helper script as a separate product surface. Most helpers exist to support one of these two lanes. + +## Current Server Modes + +### DBPM lane: public routing + +Start the server in public routing mode for DBPM-backed multitenant runs: + +```bash +cd graphql/server + +PGHOST=localhost \ +PGPORT=5432 \ +PGUSER=postgres \ +PGPASSWORD= \ +PGDATABASE=constructive \ +NODE_ENV=development \ +GRAPHILE_ENV=development \ +GRAPHQL_OBSERVABILITY_ENABLED=true \ +API_IS_PUBLIC=true \ +USE_MULTI_TENANCY_CACHE=true \ +PORT=3000 \ +NO_PROXY=localhost,127.0.0.1,::1 \ +no_proxy=localhost,127.0.0.1,::1 \ +pnpm dev +``` + +Why public mode: the DBPM flow provisions and exercises public hosts. The private `migrate` route is useful for admin/migration smoke checks, but it does not expose `SignUpInput` or `SignInInput`, and the provisioned DBPM tenant databases in this branch do not currently create private business APIs. + +### Lightweight lane: private routing + +Start the server in private/header-routing mode for the lightweight benchmark lane: + +```bash +cd graphql/server + +PGHOST=localhost \ +PGPORT=5432 \ +PGUSER=postgres \ +PGPASSWORD= \ +PGDATABASE=constructive \ +NODE_ENV=development \ +GRAPHILE_ENV=development \ +GRAPHQL_OBSERVABILITY_ENABLED=true \ +API_IS_PUBLIC=false \ +USE_MULTI_TENANCY_CACHE=true \ +PORT=3000 \ +NO_PROXY=localhost,127.0.0.1,::1 \ +no_proxy=localhost,127.0.0.1,::1 \ +pnpm dev +``` + +### Cache mode note + +`MODE=new` labels benchmark output. It does not switch an already running server. + +The server environment controls cache behavior: + +- new mode: `USE_MULTI_TENANCY_CACHE=true` +- old mode: unset `USE_MULTI_TENANCY_CACHE` and use an enlarged `GRAPHILE_CACHE_MAX` for fair comparison + +For old-mode comparisons, enlarge `GRAPHILE_CACHE_MAX` so dedicated-instance mode is not artificially dominated by LRU churn. + +## Quick Start: Lightweight Private Comparison + +Command: + +```bash +pnpm --dir graphql/server perf private-compare --k 20 --duration 300 --workers 8 +``` + +This comparison runs: + +- old mode with tuned `GRAPHILE_CACHE_MAX` +- new mode with `USE_MULTI_TENANCY_CACHE=true` +- side-by-side throughput, latency, and heap reporting + +For a very small single-mode smoke test, start the server in the desired cache mode and run: + +Command: + +```bash +pnpm --dir graphql/server perf private-benchmark --mode new --k 2 --duration 2 --workers 1 +``` + +For this branch's exact-match buildKey cache, multiple synthetic tenants with the same connection, schemas, roles, and settings should map to one handler build. Use `/debug/memory` to confirm cache behavior. + +## Quick Start: DBPM Public Smoke Run + +The public lane requires DBPM-backed setup. `API_IS_PUBLIC=true` alone is not enough; the run needs provisioned tenants, generated profiles, and public business hosts. + +### Phase 1: provision tenants and profiles + +Command: + +```bash +RUN_DIR=/tmp/constructive-perf/dbpm-$(date +%Y%m%d-%H%M%S) + +pnpm --dir graphql/server perf public-preflight \ + --run-dir "$RUN_DIR" \ + --base-url http://localhost:3000 \ + --dbpm-tenant-count 2 \ + --dbpm-shape-variants 1 \ + --auth-host auth.localhost \ + --provision-host modules.localhost \ + --business-routing-mode public \ + --business-compat-routing-mode public \ + --business-public-api-name api \ + --business-public-subdomain-prefix api-dbpm- \ + --allow-underprovisioned \ + --min-token-tenants 1 \ + --keyspace-min-route-keys 1 +``` + +Expected smoke output shape: + +```json +{ + "phase1AReady": true, + "phase1BReady": true, + "phase1CReady": true, + "phase1Ready": true, + "tokenSuccessCount": 2, + "tokenDistinctTenants": 2, + "errors": 0 +} +``` + +Warnings about `min-tenant-count`, recommended token scale, or keyspace route count are expected when using the small `--dbpm-tenant-count 2` smoke size. In that small smoke shape, `tenantReadyForPhase2` may be `false`; that is the scale gate, not a provisioning failure. Use the matching `--allow-underprovisioned` flag in phase 2. + +### Phase 2: short correctness load + +Command: + +```bash +pnpm --dir graphql/server perf public-load \ + --run-dir "$RUN_DIR" \ + --base-url http://localhost:3000 \ + --profiles "$RUN_DIR/data/business-op-profiles.json" \ + --workers 1 \ + --duration-seconds 3 \ + --idle-seconds 0 \ + --allow-underprovisioned \ + --min-tenant-count 1 \ + --disable-prewarm \ + --skip-analyze \ + --public-role anonymous +``` + +Expected output shape: + +```json +{ + "profileCount": 2, + "failed": 0 +} +``` + +`--public-role anonymous` is required for the current local public business route smoke test because the bearer token produced through `auth.localhost` is not what makes the `api-dbpm-*.localhost` table operations run as the `authenticated` database role in this local setup. + +The public access preparation should only prepare schemas whose names start with `perf-` by default. Override that safety guard only when you know exactly which schemas will be modified. + +For longer loads, increase `--workers` and `--duration-seconds`, and remove `--disable-prewarm` after the short correctness run passes. + +## Full E2E Matrix: public/private × old/new + +Use `e2e-matrix` for the full verification shape discussed in this branch: + +```text +routing modes: private, public +cache modes: old, new +k: 10 +duration: 300s +``` + +Recommended first full run: + +```bash +pnpm --dir graphql/server perf e2e-matrix \ + --routing-modes private,public \ + --cache-modes old,new \ + --k 10 \ + --duration-seconds 300 \ + --workers 4 \ + --manage-server +``` + +If that passes, run a higher-concurrency comparison: + +```bash +pnpm --dir graphql/server perf e2e-matrix \ + --routing-modes private,public \ + --cache-modes old,new \ + --k 10 \ + --duration-seconds 300 \ + --workers 10 \ + --manage-server +``` + +`--manage-server` is recommended for this full matrix. In managed mode the wrapper starts one server process per case, waits for `/debug/memory`, runs the case, captures memory before/after, then stops only the process that it started. + +Manual mode is still available by omitting `--manage-server`, but it does not switch an already-running server between old/new or public/private modes. In manual mode the operator must restart the server with the correct environment before each case; otherwise `MODE=old|new` may only label the benchmark output while all requests hit the same server configuration. + +The matrix provisions the public DBPM setup once before measured public load. For public old/new comparisons it resets generated business table data between cache modes by default. Disable that only for debugging with: + +```bash +--no-reset-between-public-cache-modes +``` + +For local `constructive-hub` runs, pass the real Postgres password from `docker-compose.yml` (or your real environment), not a redacted placeholder. A safe local pattern is: + +```bash +PGPASSWORD="$(node -e "const fs=require('fs'); const s=fs.readFileSync('docker-compose.yml','utf8'); const m=s.match(/POSTGRES_PASSWORD:\\s*([^\\s]+)/); process.stdout.write(m[1]);")" \ + pnpm --dir graphql/server perf e2e-matrix ... +``` + +For public smoke/debug runs, token keyspace route keys may collapse to `auth.localhost` even when tenant/profile counts are healthy. Keep tenant count gates strict, but lower only the token route-key gate when needed: + +```bash +--keyspace-min-route-keys 1 +``` + +The wrapper writes a summary to: + +```text +/reports/e2e-matrix-summary.json +``` + +Hard pass gates: + +- every requested case completed +- private benchmark `errors = 0` +- public load `load.failed = 0` +- case result reports exist +- `/debug/memory` before/after snapshots were captured +- public reset between cache modes succeeded when enabled + +Soft observations: + +- new-mode heap delta should generally be lower than old-mode heap delta +- new-mode QPS does not need to be a hard pass condition +- p95/p99 should not regress dramatically +- handler/cache counts in `/debug/memory` should match the expected cache mode + +## Longer Runs + +### Stress suite + +Command: + +```bash +pnpm --dir graphql/server perf stress +``` + +The stress suite is a curated multi-run private comparison harness. + +### K / tenant-count sweep + +Command: + +```bash +pnpm --dir graphql/server perf sweep \ + --k-values 3,7 \ + --duration-seconds 600 \ + --workers 16 +``` + +Public routing should use active tenant routing. Private routing can use keyspace-style synthetic expansion. + +## Shape Variants + +Shape variants are an optional modifier for the DBPM public lane, not a separate primary use case. + +The DBPM flow supports Option A shape divergence through additional provisioned tables created via the same provisioning mutation used for the main business table. + +Use this when you need to check structural divergence while keeping the main CRUD workload pointed at the original business table: + +```bash +pnpm --dir graphql/server perf public-preflight \ + --run-dir "$RUN_DIR" \ + --dbpm-tenant-count 20 \ + --dbpm-shape-variants 3 +``` + +Summarize generated shapes: + +Command: + +```bash +pnpm --dir graphql/server perf summarize-shapes \ + --manifest "$RUN_DIR/data/business-table-manifest.json" +``` + +`summarize-shapes` is a perf-local diagnostic. It is not part of the runtime handler reuse mechanism. + +## Scripts and Helpers + +| Group | Command | Backing implementation today | Notes | +|---|---|---|---| +| Private lane | `perf private-benchmark` | `e2e-benchmark.ts` | Lightweight HTTP benchmark through Express -> PostGraphile -> Grafast -> PostgreSQL | +| Private lane | `perf private-compare` | `run-comparison.sh` | Old/new comparison with tuned old-mode cache max | +| Private lane | `perf stress` | `run-stress-suite.sh` | Curated private stress matrix | +| Public lane | `perf public-preflight` | `src/legacy/phase1-preflight.ts` | DBPM validation, tenant provisioning, token/profile setup | +| Public lane | `perf public-load` | `src/legacy/phase2-load.ts` | Sustained profile-driven GraphQL business load | +| Wrapper | `perf run` | `src/commands/run.ts` | Preflight + load orchestration | +| Wrapper | `perf sweep` | `src/commands/sweep.ts` | Repeated K/tenant-count orchestration | +| Wrapper | `perf e2e-matrix` | `src/commands/e2e-matrix.ts` | public/private x old/new verification matrix with managed-server option | +| Public helper | `perf prepare-public-access` | `src/commands/prepare-public-access.ts` | Grant preparation for public business tables | +| Public helper | `perf reset-business-data` | `src/commands/reset-business-data.ts` | Truncates business workload table data | +| Public helper | internal library | `src/legacy/public-test-access-lib.ts` | Shared public access helper logic | +| Profile helper | internal library | `src/legacy/build-token-pool.ts` | Token generation for DBPM profiles | +| Profile helper | internal library | `src/legacy/build-keyspace-profiles.ts` | Route/keyspace expansion | +| Profile helper | internal library | `src/legacy/build-business-op-profiles.ts` | Business operation profile construction | +| Diagnostics | `perf summarize-shapes` | `src/commands/summarize-shapes.ts` | Name-agnostic structural shape summary | + +## Output Layout + +Most commands write to a run directory under `/tmp/constructive-perf/`: + +```text +/ +├── data/ +├── logs/ +│ ├── heap/ +│ └── sampler/ +├── reports/ +└── tmp-scripts/ +``` + +Typical artifacts include: + +- tenant credentials and token pools +- route/keyspace profiles +- business table manifests +- business operation profiles +- load summaries +- debug and memory snapshots + +## Cleanup + +Command: + +```bash +pnpm --dir graphql/server perf reset-business-data --run-dir "$RUN_DIR" +``` + +Cleanup semantics: + +- truncates generated business workload tables for a run +- does not drop DBPM-created tenant databases +- does not delete API/domain rows +- does not delete schemas or metaschema records + +Use unique `RUN_DIR` values and periodically clean old `perf-dbpm-*` data manually if the local database becomes noisy. + +## Troubleshooting + +- `Unknown type "SignUpInput"`: the DBPM phase is pointed at a private/migrate route. Use `--auth-host auth.localhost` and start the server with `API_IS_PUBLIC=true`. +- `Cannot query field "nodeType" on type "SecureTableProvision"`: old script shape. Current `SecureTableProvision` does not expose `nodeType`. +- `BAD_FIELD_INPUT`: field types must be JSON objects such as `{ "name": "text" }`, not plain strings like `"text"`. +- `phase2` route probe passes but business operations fail with missing table fields: the profile probably points at `admin-dbpm-*` or `auth-dbpm-*`. Use `--business-public-api-name api --business-public-subdomain-prefix api-dbpm-`. +- `permission denied for table items_dbpm_*`: pass `--public-role anonymous` for the current local public business route smoke test. +- Old/new comparison looks wrong: remember that `MODE=new` only labels benchmark output. Verify the server was actually started with `USE_MULTI_TENANCY_CACHE=true`. +- Local host requests unexpectedly leave the machine or fail through a proxy: export both `NO_PROXY` and `no_proxy` for localhost addresses. + +## Historical Benchmark Notes + +Older stress runs for this branch showed substantial memory savings from exact-match buildKey deduplication. + +The historical result pattern was: + +- large heap savings when many `svc_key`s collapsed onto fewer exact-match buildKeys +- modest but consistent QPS gains because the benchmark still exercises full HTTP, Grafast, and PostgreSQL work +- narrower heap savings during soak runs because repeated flushes interrupt steady-state reuse +- stable behavior under concurrency and flush churn +- no observed need to reintroduce template sharing or SQL rewriting to get meaningful wins from handler reuse + +Why heap savings can be large: in old mode, every `svc_key` keeps its own PostGraphile instance, compiled schema, and runtime caches. In new mode, `svc_key`s that resolve to the same build inputs share a handler. + +Why QPS gains are smaller than heap gains: request execution is still dominated by network, GraphQL execution, and PostgreSQL work. The new path primarily reduces working-set size and GC pressure. + +Why soak savings narrow: repeated flushes force both modes to destroy and rebuild runtime state, interrupting steady-state reuse. + +These notes are historical context and explanatory material. They are not current run instructions, and the old `postgres_perf` configuration from that report is intentionally not part of the main quick start. diff --git a/graphql/server/perf/e2e-benchmark.ts b/graphql/server/perf/e2e-benchmark.ts new file mode 100644 index 0000000000..c0100d7af9 --- /dev/null +++ b/graphql/server/perf/e2e-benchmark.ts @@ -0,0 +1,455 @@ +#!/usr/bin/env npx ts-node +/** + * E2E GraphQL Multi-Tenancy Benchmark + * + * Sends REAL GraphQL HTTP requests through the Express server, + * exercising the full PostGraphile/Grafast pipeline. + * + * Usage: + * MODE=old|new K=30 DURATION=300 WORKERS=8 npx ts-node perf/e2e-benchmark.ts + */ + +import http from 'http'; +import fs from 'fs'; +import path from 'path'; + +// ─── Configuration ────────────────────────────────────────────────────────── +const K = parseInt(process.env.K || '30', 10); +const DURATION_SEC = parseInt(process.env.DURATION || '300', 10); +const WORKERS = parseInt(process.env.WORKERS || '8', 10); +const SERVER_PORT = parseInt(process.env.SERVER_PORT || '3000', 10); +const SERVER_HOST = process.env.SERVER_HOST || 'localhost'; +const MODE = process.env.MODE || 'old'; // 'old' or 'new' +const API_IS_PUBLIC = process.env.API_IS_PUBLIC === 'true'; +const PUBLIC_HOSTS = (process.env.E2E_PUBLIC_HOSTS || process.env.PUBLIC_HOSTS || '') + .split(',') + .map((host) => host.trim()) + .filter(Boolean); + +// Schemas to expose per tenant (via X-Schemata header) +const SCHEMAS = 'services_public'; + +// ─── Types ────────────────────────────────────────────────────────────────── +interface TenantProfile { + tenantId: string; + databaseId: string; + headers: Record; +} + +interface OperationProfile { + name: string; + weight: number; + query: string; + variables?: Record; +} + +interface WorkerStats { + totalQueries: number; + errors: number; + latencies: number[]; + errorSamples: string[]; +} + +interface BenchmarkResult { + mode: string; + k: number; + durationSec: number; + workers: number; + totalQueries: number; + errors: number; + qps: number; + p50: number; + p95: number; + p99: number; + heapBefore: number; + heapAfter: number; + heapDelta: number; + coldStartMs: number[]; +} + +// ─── HTTP Client ──────────────────────────────────────────────────────────── +const agent = new http.Agent({ + keepAlive: true, + maxSockets: WORKERS * 4, + maxFreeSockets: WORKERS * 2, +}); + +function graphqlRequest( + query: string, + headers: Record, + variables?: Record, +): Promise<{ data?: unknown; errors?: unknown[]; latencyMs: number }> { + return new Promise((resolve, reject) => { + const body = JSON.stringify({ query, variables }); + const start = performance.now(); + + const req = http.request( + { + hostname: SERVER_HOST, + port: SERVER_PORT, + path: '/graphql', + method: 'POST', + agent, + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(body), + ...headers, + }, + }, + (res) => { + let data = ''; + res.on('data', (chunk) => (data += chunk)); + res.on('end', () => { + const latencyMs = performance.now() - start; + try { + const parsed = JSON.parse(data); + resolve({ ...parsed, latencyMs }); + } catch { + resolve({ errors: [{ message: `Parse error: ${data.slice(0, 200)}` }], latencyMs }); + } + }); + }, + ); + req.on('error', (err) => reject(err)); + req.write(body); + req.end(); + }); +} + +// ─── Memory Snapshot ──────────────────────────────────────────────────────── +async function getHeapUsedMB(): Promise { + try { + const res = await new Promise((resolve, reject) => { + http + .get(`http://${SERVER_HOST}:${SERVER_PORT}/debug/memory`, (res) => { + let data = ''; + res.on('data', (chunk) => (data += chunk)); + res.on('end', () => resolve(data)); + }) + .on('error', reject); + }); + const parsed = JSON.parse(res); + const heapUsedBytes = + typeof parsed?.memory?.heapUsedBytes === 'number' + ? parsed.memory.heapUsedBytes + : typeof parsed?.heapUsedBytes === 'number' + ? parsed.heapUsedBytes + : typeof parsed?.heapUsed === 'number' + ? parsed.heapUsed + : null; + if (heapUsedBytes !== null) return heapUsedBytes / 1024 / 1024; + } catch { + // fallback + } + return process.memoryUsage().heapUsed / 1024 / 1024; +} + +// ─── Tenant Profiles ──────────────────────────────────────────────────────── +function buildTenantProfiles(k: number): TenantProfile[] { + const profiles: TenantProfile[] = []; + if (API_IS_PUBLIC) { + if (PUBLIC_HOSTS.length === 0) { + throw new Error('API_IS_PUBLIC=true requires E2E_PUBLIC_HOSTS or PUBLIC_HOSTS'); + } + + for (let i = 0; i < k; i++) { + const host = PUBLIC_HOSTS[i % PUBLIC_HOSTS.length]; + profiles.push({ + tenantId: `public-${i}-${host}`, + databaseId: host, + headers: { + Host: host, + }, + }); + } + return profiles; + } + + for (let i = 0; i < k; i++) { + const tenantId = `tenant-${i}`; + const databaseId = `db-${i.toString().padStart(4, '0')}-${tenantId}`; + profiles.push({ + tenantId, + databaseId, + headers: { + 'X-Schemata': SCHEMAS, + 'X-Database-Id': databaseId, + }, + }); + } + return profiles; +} + +// ─── Operation Profiles ───────────────────────────────────────────────────── +function buildOperationProfiles(): OperationProfile[] { + if (API_IS_PUBLIC) { + return [ + { + name: 'Typename', + weight: 60, + query: `query Typename { __typename }`, + }, + { + name: 'QueryType', + weight: 20, + query: `query QueryType { __schema { queryType { name } } }`, + }, + { + name: 'TypeList', + weight: 20, + query: `query TypeList { __schema { types { name kind } } }`, + }, + ]; + } + + return [ + { + name: 'ListApis', + weight: 40, + query: `query ListApis { apis(first: 10) { nodes { id name dbname isPublic } totalCount } }`, + }, + { + name: 'ListApps', + weight: 20, + query: `query ListApps { apps(first: 10) { nodes { id name databaseId } totalCount } }`, + }, + { + name: 'ListDomains', + weight: 20, + query: `query ListDomains { domains(first: 10) { nodes { id domain subdomain } totalCount } }`, + }, + { + name: 'Introspection', + weight: 10, + query: `query Introspect { __schema { queryType { name } mutationType { name } types { name kind } } }`, + }, + { + name: 'MetaQuery', + weight: 10, + query: `query Meta { _meta { tables { name schemaName fields { name } } } }`, + }, + ]; +} + +function pickWeightedOperation(profiles: OperationProfile[]): OperationProfile { + const totalWeight = profiles.reduce((sum, p) => sum + p.weight, 0); + let r = Math.random() * totalWeight; + for (const p of profiles) { + r -= p.weight; + if (r <= 0) return p; + } + return profiles[profiles.length - 1]; +} + +// ─── Cold Start: Warm up all tenants ──────────────────────────────────────── +async function warmUpTenants(tenants: TenantProfile[], operations: OperationProfile[]): Promise { + const coldStartMs: number[] = []; + const simpleQuery = operations[0]; // ListApis + + console.log(`\n[Phase 1] Warming up ${tenants.length} tenants (cold start)...`); + for (const tenant of tenants) { + const start = performance.now(); + const result = await graphqlRequest(simpleQuery.query, tenant.headers, simpleQuery.variables); + const elapsed = performance.now() - start; + coldStartMs.push(elapsed); + + if (result.errors) { + console.error(` ERROR warming tenant ${tenant.tenantId}:`, JSON.stringify(result.errors).slice(0, 200)); + } else { + console.log(` ${tenant.tenantId}: ${elapsed.toFixed(1)}ms (cold start)`); + } + } + return coldStartMs; +} + +// ─── Pressure Worker ──────────────────────────────────────────────────────── +async function pressureWorker( + tenants: TenantProfile[], + operations: OperationProfile[], + durationMs: number, + _workerId: number, +): Promise { + const stats: WorkerStats = { totalQueries: 0, errors: 0, latencies: [], errorSamples: [] }; + const endTime = Date.now() + durationMs; + + while (Date.now() < endTime) { + const tenant = tenants[Math.floor(Math.random() * tenants.length)]; + const op = pickWeightedOperation(operations); + + try { + const result = await graphqlRequest(op.query, tenant.headers, op.variables); + stats.totalQueries++; + stats.latencies.push(result.latencyMs); + + if (result.errors) { + stats.errors++; + if (stats.errorSamples.length < 3) { + stats.errorSamples.push(`[${op.name}] ${JSON.stringify(result.errors).slice(0, 200)}`); + } + } + } catch (err) { + stats.errors++; + stats.totalQueries++; + if (stats.errorSamples.length < 3) { + stats.errorSamples.push(`[${op.name}] ${String(err).slice(0, 200)}`); + } + } + } + + return stats; +} + +// ─── Percentile Calculator ────────────────────────────────────────────────── +function percentile(sorted: number[], p: number): number { + if (sorted.length === 0) return 0; + const idx = Math.ceil((p / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; +} + +// ─── Main Benchmark ───────────────────────────────────────────────────────── +async function runBenchmark(): Promise { + console.log('='.repeat(70)); + console.log(`E2E GraphQL Benchmark — Mode: ${MODE.toUpperCase()}`); + console.log(` K=${K} tenants, Duration=${DURATION_SEC}s, Workers=${WORKERS}`); + console.log(` Server: http://${SERVER_HOST}:${SERVER_PORT}`); + console.log(` Schemas: ${SCHEMAS}`); + console.log(` API_IS_PUBLIC=${API_IS_PUBLIC}`); + if (API_IS_PUBLIC) { + console.log(` Public hosts: ${PUBLIC_HOSTS.length}`); + } + console.log('='.repeat(70)); + + const tenants = buildTenantProfiles(K); + const operations = buildOperationProfiles(); + + // Phase 0: Pre-benchmark heap snapshot + const heapBefore = await getHeapUsedMB(); + console.log(`\n[Phase 0] Heap before warmup: ${heapBefore.toFixed(2)} MB`); + + // Phase 1: Cold start — warm up all tenants + const coldStartMs = await warmUpTenants(tenants, operations); + + // Second warm-up pass to ensure operation plan caches are populated + console.log(`\n[Phase 1b] Second warm-up pass (populate operation plan caches)...`); + for (const tenant of tenants) { + for (const op of operations) { + await graphqlRequest(op.query, tenant.headers, op.variables); + } + } + + // Phase 2: Heap snapshot after warmup + const heapAfterWarmup = await getHeapUsedMB(); + console.log(`\n[Phase 2] Heap after warmup: ${heapAfterWarmup.toFixed(2)} MB`); + + // Phase 3: Sustained pressure test + const durationMs = DURATION_SEC * 1000; + console.log(`\n[Phase 3] Starting ${WORKERS} workers for ${DURATION_SEC}s sustained load...`); + const startTime = Date.now(); + + const workerPromises: Promise[] = []; + for (let w = 0; w < WORKERS; w++) { + workerPromises.push(pressureWorker(tenants, operations, durationMs, w)); + } + + // Progress reporting + const progressInterval = setInterval(() => { + const elapsed = ((Date.now() - startTime) / 1000).toFixed(0); + const remaining = Math.max(0, DURATION_SEC - parseInt(elapsed)); + process.stdout.write(`\r Elapsed: ${elapsed}s / ${DURATION_SEC}s (${remaining}s remaining) `); + }, 5000); + + const results = await Promise.all(workerPromises); + clearInterval(progressInterval); + console.log('\n'); + + // Phase 4: Post-pressure heap snapshot + const heapAfter = await getHeapUsedMB(); + + // Aggregate stats + let totalQueries = 0; + let totalErrors = 0; + const allLatencies: number[] = []; + const allErrorSamples: string[] = []; + + for (const r of results) { + totalQueries += r.totalQueries; + totalErrors += r.errors; + for (const latency of r.latencies) { + allLatencies.push(latency); + } + allErrorSamples.push(...r.errorSamples); + } + + if (allErrorSamples.length > 0) { + console.log(`\n[Errors] Sample error messages (first ${Math.min(allErrorSamples.length, 5)}):`); + for (const s of allErrorSamples.slice(0, 5)) { + console.log(` ${s}`); + } + } + + allLatencies.sort((a, b) => a - b); + const actualDuration = (Date.now() - startTime) / 1000; + const qps = totalQueries / actualDuration; + + const result: BenchmarkResult = { + mode: MODE, + k: K, + durationSec: Math.round(actualDuration), + workers: WORKERS, + totalQueries, + errors: totalErrors, + qps: Math.round(qps), + p50: Math.round(percentile(allLatencies, 50)), + p95: Math.round(percentile(allLatencies, 95)), + p99: Math.round(percentile(allLatencies, 99)), + heapBefore: Math.round(heapBefore * 100) / 100, + heapAfter: Math.round(heapAfter * 100) / 100, + heapDelta: Math.round((heapAfter - heapBefore) * 100) / 100, + coldStartMs: coldStartMs.map((v) => Math.round(v)), + }; + + // Print results + console.log('\u2500'.repeat(70)); + console.log('RESULTS:'); + console.log('\u2500'.repeat(70)); + console.log(` Mode: ${result.mode.toUpperCase()}`); + console.log(` Tenants (k): ${result.k}`); + console.log(` Duration: ${result.durationSec}s`); + console.log(` Workers: ${result.workers}`); + console.log(` Total Queries: ${result.totalQueries.toLocaleString()}`); + console.log(` Errors: ${result.errors}`); + console.log(` QPS: ${result.qps.toLocaleString()}`); + console.log(` p50 Latency: ${result.p50}ms`); + console.log(` p95 Latency: ${result.p95}ms`); + console.log(` p99 Latency: ${result.p99}ms`); + console.log(` Heap Before: ${result.heapBefore} MB`); + console.log(` Heap After: ${result.heapAfter} MB`); + console.log(` Heap Delta: ${result.heapDelta} MB`); + console.log( + ` Cold Start (first/last): ${result.coldStartMs[0]}ms / ${result.coldStartMs[result.coldStartMs.length - 1]}ms`, + ); + console.log('\u2500'.repeat(70)); + + // Write result to JSON file + const resultsDir = path.join(__dirname, 'results'); + fs.mkdirSync(resultsDir, { recursive: true }); + const outFile = path.join(resultsDir, `e2e-benchmark-${MODE}-k${K}.json`); + fs.writeFileSync(outFile, JSON.stringify(result, null, 2)); + console.log(`\nResults written to ${outFile}`); + + // Also write to /tmp for compatibility with run scripts + const tmpFile = `/tmp/e2e-benchmark-${MODE}-k${K}.json`; + fs.writeFileSync(tmpFile, JSON.stringify(result, null, 2)); + + return result; +} + +// ─── Entry Point ──────────────────────────────────────────────────────────── +runBenchmark() + .then((result) => { + process.exit(result.errors > 0 ? 1 : 0); + }) + .catch((err) => { + console.error('Benchmark failed:', err); + process.exit(2); + }); diff --git a/graphql/server/perf/run-comparison.sh b/graphql/server/perf/run-comparison.sh new file mode 100755 index 0000000000..5ca5e6fed7 --- /dev/null +++ b/graphql/server/perf/run-comparison.sh @@ -0,0 +1,281 @@ +#!/usr/bin/env bash +# run-comparison.sh — Old vs New multi-tenancy comparison using the perf framework +# +# Usage: +# bash graphql/server/perf/run-comparison.sh [--k 20] [--duration 300] [--workers 8] +# +# This script runs the full e2e comparison: +# 1. Starts server in OLD mode (dedicated instances) with enlarged GRAPHILE_CACHE_MAX +# 2. Runs phase2 load test +# 3. Stops server +# 4. Starts server in NEW mode (multi-tenancy cache) +# 5. Runs phase2 load test +# 6. Compares results +# +# IMPORTANT: For the old approach, GRAPHILE_CACHE_MAX is enlarged to prevent +# cache eviction churn that would artificially penalize the dedicated-instance mode. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SERVER_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$SERVER_DIR/../.." && pwd)" + +# Defaults +K="${K:-20}" +DURATION="${DURATION:-300}" +WORKERS="${WORKERS:-8}" +IDLE_SECONDS="${IDLE_SECONDS:-30}" +SERVER_PORT="${SERVER_PORT:-3000}" +BASE_URL="http://localhost:${SERVER_PORT}" +RUN_DIR="${RUN_DIR:-/tmp/constructive-perf/comparison-$(date +%Y%m%dT%H%M%S)}" +STOP_WAIT_SECONDS="${STOP_WAIT_SECONDS:-10}" + +# Parse CLI args +while [[ $# -gt 0 ]]; do + case "$1" in + --k) K="$2"; shift 2 ;; + --duration) DURATION="$2"; shift 2 ;; + --workers) WORKERS="$2"; shift 2 ;; + --idle) IDLE_SECONDS="$2"; shift 2 ;; + --port) SERVER_PORT="$2"; BASE_URL="http://localhost:${SERVER_PORT}"; shift 2 ;; + --run-dir) RUN_DIR="$2"; shift 2 ;; + *) echo "Unknown arg: $1"; exit 1 ;; + esac +done + +# Enlarge GRAPHILE_CACHE_MAX for old approach to prevent unfair eviction churn. +# With k=20 tenants × 3 endpoints, the old cache needs at least 60 slots. +# We use 2x headroom: max(100, k*6). +OLD_CACHE_MAX=$(( K * 6 )) +if [ "$OLD_CACHE_MAX" -lt 100 ]; then + OLD_CACHE_MAX=100 +fi + +# Common env +export PGHOST="${PGHOST:-localhost}" +export PGPORT="${PGPORT:-5432}" +export PGUSER="${PGUSER:-postgres}" +export PGPASSWORD="${PGPASSWORD:-password}" +export PGDATABASE="${PGDATABASE:-constructive}" +export PORT="$SERVER_PORT" +export NO_PROXY="${NO_PROXY:-localhost,127.0.0.1,::1}" +export no_proxy="${no_proxy:-$NO_PROXY}" +export NODE_ENV=development +export GRAPHILE_ENV=development +export GRAPHQL_OBSERVABILITY_ENABLED=true +export API_IS_PUBLIC="${API_IS_PUBLIC:-false}" +export K DURATION WORKERS SERVER_PORT + +echo "==============================================================" +echo "E2E Multi-Tenancy Comparison (Perf Framework)" +echo " K=$K tenants, Duration=${DURATION}s, Workers=$WORKERS" +echo " API_IS_PUBLIC=$API_IS_PUBLIC" +echo " Old approach GRAPHILE_CACHE_MAX=$OLD_CACHE_MAX" +echo " Run dir: $RUN_DIR" +echo "==============================================================" + +mkdir -p "$RUN_DIR" + +kill_server() { + local pids + pids="$(lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN 2>/dev/null || true)" + if [ -n "$pids" ]; then + kill $pids 2>/dev/null || true + for _ in $(seq 1 "$STOP_WAIT_SECONDS"); do + if ! lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN >/dev/null 2>&1; then + for pid in $pids; do wait "$pid" 2>/dev/null || true; done + if [ -n "${SERVER_PID:-}" ]; then wait "$SERVER_PID" 2>/dev/null || true; fi + return 0 + fi + sleep 1 + done + + pids="$(lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN 2>/dev/null || true)" + if [ -n "$pids" ]; then + kill -9 $pids 2>/dev/null || true + for pid in $pids; do wait "$pid" 2>/dev/null || true; done + if [ -n "${SERVER_PID:-}" ]; then wait "$SERVER_PID" 2>/dev/null || true; fi + fi + fi +} + +trap kill_server EXIT + +wait_for_server() { + local max_wait=90 + local waited=0 + echo -n " Waiting for server on port $SERVER_PORT..." + while ! curl --noproxy '*' -sf "${BASE_URL}/debug/memory" >/dev/null 2>&1; do + if [ -n "${SERVER_PID:-}" ] && ! kill -0 "$SERVER_PID" 2>/dev/null; then + echo " failed (server process exited)" + return 1 + fi + sleep 1 + waited=$((waited + 1)) + if [ $waited -ge $max_wait ]; then + echo " TIMEOUT after ${max_wait}s" + return 1 + fi + echo -n "." + done + if [ -n "${SERVER_PID:-}" ] && ! kill -0 "$SERVER_PID" 2>/dev/null; then + echo " failed (server process exited)" + return 1 + fi + echo " ready (${waited}s)" +} + +start_server() { + local mode="$1" + echo "" + echo "--------------------------------------------------------------" + echo "Starting server in ${mode} mode..." + echo "--------------------------------------------------------------" + + kill_server + + if [ "$mode" = "new" ]; then + export USE_MULTI_TENANCY_CACHE=true + unset GRAPHILE_CACHE_MAX 2>/dev/null || true + echo " USE_MULTI_TENANCY_CACHE=true (buildKey handler reuse)" + else + unset USE_MULTI_TENANCY_CACHE 2>/dev/null || true + export GRAPHILE_CACHE_MAX="$OLD_CACHE_MAX" + echo " GRAPHILE_CACHE_MAX=$OLD_CACHE_MAX (enlarged for fair comparison)" + fi + + cd "$SERVER_DIR" + npx ts-node src/run.ts > "$RUN_DIR/server-${mode}.log" 2>&1 & + SERVER_PID=$! + echo " Server PID: $SERVER_PID" + + wait_for_server +} + +run_e2e_benchmark() { + local mode="$1" + local tier="e2e-${mode}-k${K}" + local mode_label + mode_label="$(printf '%s' "$mode" | tr '[:lower:]' '[:upper:]')" + + echo "" + echo "==============================================================" + echo "Running ${mode_label} mode benchmark (k=$K, ${DURATION}s, ${WORKERS} workers)" + echo "==============================================================" + + cd "$SERVER_DIR" + + # Use the e2e-benchmark.ts from perf/ directory + MODE="$mode" K="$K" DURATION="$DURATION" WORKERS="$WORKERS" \ + SERVER_PORT="$SERVER_PORT" \ + npx ts-node perf/e2e-benchmark.ts 2>&1 | tee "$RUN_DIR/benchmark-${mode}-output.txt" + + echo " ${mode_label} mode complete." +} + +# Capture server memory snapshot +capture_memory() { + local label="$1" + local outfile="$RUN_DIR/memory-${label}.json" + curl --noproxy '*' -sf "${BASE_URL}/debug/memory" > "$outfile" 2>/dev/null || echo '{"error":"failed"}' > "$outfile" + echo " Memory snapshot: $outfile" +} + +compare_results() { + echo "" + echo "==============================================================" + echo "COMPARISON: OLD (Dedicated) vs NEW (Multi-tenancy Cache)" + echo "==============================================================" + + local old_file="/tmp/e2e-benchmark-old-k${K}.json" + local new_file="/tmp/e2e-benchmark-new-k${K}.json" + + if [ ! -f "$old_file" ] || [ ! -f "$new_file" ]; then + echo " ERROR: Missing result files" + echo " Expected: $old_file and $new_file" + return 1 + fi + + # Copy results to run dir + cp "$old_file" "$RUN_DIR/" 2>/dev/null || true + cp "$new_file" "$RUN_DIR/" 2>/dev/null || true + + python3 << 'PYEOF' +import json, sys, os + +k = int(os.environ.get('K', '20')) + +with open(f"/tmp/e2e-benchmark-old-k{k}.json") as f: + old = json.load(f) +with open(f"/tmp/e2e-benchmark-new-k{k}.json") as f: + new = json.load(f) + +def fmt(v, unit=""): + if isinstance(v, float): + return f"{v:.2f}{unit}" + return f"{v:,}{unit}" + +def delta(o, n, unit="", lower_better=True): + if o == 0: + return "N/A" + diff = n - o + pct = (diff / o) * 100 + return f"{diff:+.1f}{unit} ({pct:+.1f}%)" + +print() +print(f"{'Metric':<25} {'Dedicated (Old)':<20} {'Multi-tenant (New)':<20} {'Delta':<30}") +print("-" * 95) +print(f"{'Tenants (k)':<25} {fmt(old['k']):<20} {fmt(new['k']):<20}") +print(f"{'Duration':<25} {fmt(old['durationSec'], 's'):<20} {fmt(new['durationSec'], 's'):<20}") +print(f"{'Workers':<25} {fmt(old['workers']):<20} {fmt(new['workers']):<20}") +print(f"{'Total Queries':<25} {fmt(old['totalQueries']):<20} {fmt(new['totalQueries']):<20} {delta(old['totalQueries'], new['totalQueries'], '', False)}") +print(f"{'Errors':<25} {fmt(old['errors']):<20} {fmt(new['errors']):<20}") +print(f"{'QPS':<25} {fmt(old['qps']):<20} {fmt(new['qps']):<20} {delta(old['qps'], new['qps'], '', False)}") +print(f"{'p50 Latency':<25} {fmt(old['p50'], 'ms'):<20} {fmt(new['p50'], 'ms'):<20} {delta(old['p50'], new['p50'], 'ms', True)}") +print(f"{'p95 Latency':<25} {fmt(old['p95'], 'ms'):<20} {fmt(new['p95'], 'ms'):<20} {delta(old['p95'], new['p95'], 'ms', True)}") +print(f"{'p99 Latency':<25} {fmt(old['p99'], 'ms'):<20} {fmt(new['p99'], 'ms'):<20} {delta(old['p99'], new['p99'], 'ms', True)}") +print(f"{'Heap Before':<25} {fmt(old['heapBefore'], ' MB'):<20} {fmt(new['heapBefore'], ' MB'):<20}") +print(f"{'Heap After':<25} {fmt(old['heapAfter'], ' MB'):<20} {fmt(new['heapAfter'], ' MB'):<20}") +print(f"{'Heap Delta':<25} {fmt(old['heapDelta'], ' MB'):<20} {fmt(new['heapDelta'], ' MB'):<20} {delta(old['heapDelta'], new['heapDelta'], ' MB', True)}") +print() + +old_cold = old.get('coldStartMs', []) +new_cold = new.get('coldStartMs', []) +if old_cold and new_cold: + print(f"{'Cold Start (1st)':<25} {fmt(old_cold[0], 'ms'):<20} {fmt(new_cold[0], 'ms'):<20}") + print(f"{'Cold Start (last)':<25} {fmt(old_cold[-1], 'ms'):<20} {fmt(new_cold[-1], 'ms'):<20}") + if len(new_cold) > 1: + new_avg2 = sum(new_cold[1:]) / len(new_cold[1:]) + old_avg2 = sum(old_cold[1:]) / len(old_cold[1:]) + print(f"{'Cold Start (2nd+ avg)':<25} {fmt(old_avg2, 'ms'):<20} {fmt(new_avg2, 'ms'):<20} {delta(old_avg2, new_avg2, 'ms', True)}") + +print() +print("-" * 95) +PYEOF +} + +# ─── Main Flow ─────────────────────────────────────────────────────────────── + +# Phase A: OLD mode (dedicated PostGraphile instances with enlarged cache) +start_server "old" +capture_memory "old-before" +run_e2e_benchmark "old" +capture_memory "old-after" +kill_server + +# Phase B: NEW mode (multi-tenancy cache with buildKey handler reuse) +start_server "new" +capture_memory "new-before" +run_e2e_benchmark "new" +capture_memory "new-after" +kill_server + +# Phase C: Compare +compare_results + +echo "" +echo "Comparison complete. Results in: $RUN_DIR" +echo " Server logs: $RUN_DIR/server-{old,new}.log" +echo " Memory snapshots: $RUN_DIR/memory-*.json" +echo " Benchmark output: $RUN_DIR/benchmark-*-output.txt" diff --git a/graphql/server/perf/run-e2e-benchmark.sh b/graphql/server/perf/run-e2e-benchmark.sh new file mode 100755 index 0000000000..19bd757eb6 --- /dev/null +++ b/graphql/server/perf/run-e2e-benchmark.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# run-e2e-benchmark.sh — Run a single-mode e2e benchmark +# +# Usage: +# bash graphql/server/perf/run-e2e-benchmark.sh [--mode new] [--k 30] [--duration 300] [--workers 8] +# +# For comparison (old vs new), use run-comparison.sh instead. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SERVER_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +# Defaults +MODE="${MODE:-new}" +K="${K:-30}" +DURATION="${DURATION:-300}" +WORKERS="${WORKERS:-8}" +SERVER_PORT="${SERVER_PORT:-3000}" +BASE_URL="http://localhost:${SERVER_PORT}" +STOP_WAIT_SECONDS="${STOP_WAIT_SECONDS:-10}" + +# Parse CLI args +while [[ $# -gt 0 ]]; do + case "$1" in + --mode) MODE="$2"; shift 2 ;; + --k) K="$2"; shift 2 ;; + --duration) DURATION="$2"; shift 2 ;; + --workers) WORKERS="$2"; shift 2 ;; + --port) SERVER_PORT="$2"; BASE_URL="http://localhost:${SERVER_PORT}"; shift 2 ;; + *) echo "Unknown arg: $1"; exit 1 ;; + esac +done + +# Common env +export PGHOST="${PGHOST:-localhost}" +export PGPORT="${PGPORT:-5432}" +export PGUSER="${PGUSER:-postgres}" +export PGPASSWORD="${PGPASSWORD:-password}" +export PGDATABASE="${PGDATABASE:-constructive}" +export PORT="$SERVER_PORT" +export NO_PROXY="${NO_PROXY:-localhost,127.0.0.1,::1}" +export no_proxy="${no_proxy:-$NO_PROXY}" +export NODE_ENV=development +export GRAPHILE_ENV=development +export GRAPHQL_OBSERVABILITY_ENABLED=true +export API_IS_PUBLIC="${API_IS_PUBLIC:-false}" +MODE_LABEL="$(printf '%s' "$MODE" | tr '[:lower:]' '[:upper:]')" + +echo "==============================================================" +echo "E2E Multi-Tenancy Benchmark (Single Mode)" +echo " Mode=$MODE, K=$K tenants, Duration=${DURATION}s, Workers=$WORKERS" +echo " API_IS_PUBLIC=$API_IS_PUBLIC" +echo "==============================================================" + +kill_server() { + local pids + pids="$(lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN 2>/dev/null || true)" + if [ -n "$pids" ]; then + kill $pids 2>/dev/null || true + for _ in $(seq 1 "$STOP_WAIT_SECONDS"); do + if ! lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN >/dev/null 2>&1; then + for pid in $pids; do wait "$pid" 2>/dev/null || true; done + if [ -n "${SERVER_PID:-}" ]; then wait "$SERVER_PID" 2>/dev/null || true; fi + return 0 + fi + sleep 1 + done + + pids="$(lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN 2>/dev/null || true)" + if [ -n "$pids" ]; then + kill -9 $pids 2>/dev/null || true + for pid in $pids; do wait "$pid" 2>/dev/null || true; done + if [ -n "${SERVER_PID:-}" ]; then wait "$SERVER_PID" 2>/dev/null || true; fi + fi + fi +} + +wait_for_server() { + local max_wait=120 + local waited=0 + echo -n " Waiting for server on port $SERVER_PORT..." + while ! curl --noproxy '*' -sf "${BASE_URL}/debug/memory" >/dev/null 2>&1; do + if [ -n "${SERVER_PID:-}" ] && ! kill -0 "$SERVER_PID" 2>/dev/null; then + echo " failed (server process exited)" + return 1 + fi + sleep 1 + waited=$((waited + 1)) + if [ $waited -ge $max_wait ]; then + echo " TIMEOUT after ${max_wait}s" + return 1 + fi + echo -n "." + done + if [ -n "${SERVER_PID:-}" ] && ! kill -0 "$SERVER_PID" 2>/dev/null; then + echo " failed (server process exited)" + return 1 + fi + echo " ready (${waited}s)" +} + +kill_server +trap kill_server EXIT + +if [ "$MODE" = "new" ]; then + export USE_MULTI_TENANCY_CACHE=true + unset GRAPHILE_CACHE_MAX 2>/dev/null || true + echo " USE_MULTI_TENANCY_CACHE=true (buildKey handler reuse)" +else + unset USE_MULTI_TENANCY_CACHE 2>/dev/null || true + OLD_CACHE_MAX=$(( K * 6 )) + if [ "$OLD_CACHE_MAX" -lt 100 ]; then + OLD_CACHE_MAX=100 + fi + export GRAPHILE_CACHE_MAX="$OLD_CACHE_MAX" + echo " GRAPHILE_CACHE_MAX=$OLD_CACHE_MAX (enlarged for fair comparison)" +fi + +cd "$SERVER_DIR" +npx ts-node src/run.ts > /tmp/server-${MODE}.log 2>&1 & +SERVER_PID=$! +echo " Server PID: $SERVER_PID" + +wait_for_server + +echo "" +echo "==============================================================" +echo "Running ${MODE_LABEL} mode benchmark (k=$K, ${DURATION}s, ${WORKERS} workers)" +echo "==============================================================" + +MODE="$MODE" K="$K" DURATION="$DURATION" WORKERS="$WORKERS" \ + SERVER_PORT="$SERVER_PORT" \ + npx ts-node perf/e2e-benchmark.ts + +echo "" +echo "Benchmark complete." + +# Capture final memory +curl --noproxy '*' -sf "${BASE_URL}/debug/memory" > /tmp/memory-${MODE}-final.json 2>/dev/null || true + +kill_server + +echo "Server stopped. Results in perf/results/ and /tmp/" diff --git a/graphql/server/perf/run-stress-suite.sh b/graphql/server/perf/run-stress-suite.sh new file mode 100644 index 0000000000..542077577a --- /dev/null +++ b/graphql/server/perf/run-stress-suite.sh @@ -0,0 +1,175 @@ +#!/bin/bash +# run-stress-suite.sh — Run all 6 stress tests (OLD vs NEW) sequentially +# Usage: bash perf/run-stress-suite.sh +# +# Requires: server NOT running (this script manages server lifecycle) +# Requires: a deployed Constructive database; defaults to PGDATABASE=constructive + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SERVER_DIR="$(dirname "$SCRIPT_DIR")" +cd "$SERVER_DIR" + +# Common env +export PGHOST="${PGHOST:-127.0.0.1}" PGPORT="${PGPORT:-5432}" PGUSER="${PGUSER:-postgres}" PGPASSWORD="${PGPASSWORD:-password}" PGDATABASE="${PGDATABASE:-constructive}" +export NODE_ENV=development GRAPHILE_ENV=development API_IS_PUBLIC=false GRAPHQL_OBSERVABILITY_ENABLED=true +export SERVER_PORT=3000 +export PORT="$SERVER_PORT" +export STOP_WAIT_SECONDS="${STOP_WAIT_SECONDS:-10}" +export NO_PROXY="${NO_PROXY:-localhost,127.0.0.1,::1}" +export no_proxy="${no_proxy:-$NO_PROXY}" + +RESULTS_FILE="/tmp/stress-suite-results.jsonl" +> "$RESULTS_FILE" + +kill_server() { + local pids + pids="$(lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN 2>/dev/null || true)" + if [ -n "$pids" ]; then + kill $pids 2>/dev/null || true + for _ in $(seq 1 "$STOP_WAIT_SECONDS"); do + if ! lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN >/dev/null 2>&1; then + for pid in $pids; do wait "$pid" 2>/dev/null || true; done + if [ -n "${SERVER_PID:-}" ]; then wait "$SERVER_PID" 2>/dev/null || true; fi + return 0 + fi + sleep 1 + done + + pids="$(lsof -tiTCP:"${SERVER_PORT}" -sTCP:LISTEN 2>/dev/null || true)" + if [ -n "$pids" ]; then + kill -9 $pids 2>/dev/null || true + for pid in $pids; do wait "$pid" 2>/dev/null || true; done + if [ -n "${SERVER_PID:-}" ]; then wait "$SERVER_PID" 2>/dev/null || true; fi + fi + fi +} + +trap kill_server EXIT + +start_server() { + local mode="$1" + local cache_max="${2:-}" + kill_server + + local env_extra="" + if [ "$mode" = "new" ]; then + env_extra="USE_MULTI_TENANCY_CACHE=true" + elif [ -n "$cache_max" ]; then + env_extra="GRAPHILE_CACHE_MAX=$cache_max" + fi + + echo ">>> Starting server (mode=$mode, extra=$env_extra)..." + eval "$env_extra npx ts-node src/run.ts" & + SERVER_PID=$! + + # Wait for server to be ready + for i in $(seq 1 30); do + if ! kill -0 "$SERVER_PID" 2>/dev/null; then + echo ">>> Server process exited before becoming ready" + return 1 + fi + if curl --noproxy '*' -sf http://localhost:$SERVER_PORT/debug/memory > /dev/null 2>&1; then + echo ">>> Server ready" + return 0 + fi + sleep 1 + done + echo ">>> Server failed to start!" + return 1 +} + +capture_memory() { + curl --noproxy '*' -sf http://localhost:$SERVER_PORT/debug/memory 2>/dev/null || echo '{}' +} + +run_test() { + local test_name="$1" + local mode="$2" + local cache_max="$3" + shift 3 + # remaining args are env vars for the benchmark + + echo "" + echo "================================================================" + echo " TEST: $test_name — MODE: $mode" + echo "================================================================" + + start_server "$mode" "$cache_max" + + echo ">>> Running benchmark: $*" + env "$@" MODE=$mode TEST_NAME=$test_name npx ts-node perf/e2e-benchmark.ts || true + + echo ">>> Final memory snapshot:" + capture_memory | python3 -c " +import sys,json +try: + d=json.load(sys.stdin) + m=d.get('memory',d) + heap=m.get('heapUsedBytes', m.get('heapUsed', 0)) + rss=m.get('rssBytes', m.get('rss', 0)) + if isinstance(heap, str): heap=0 + if isinstance(rss, str): rss=0 + print(f' Heap: {heap/1024/1024:.1f} MB, RSS: {rss/1024/1024:.1f} MB') + gb=d.get('graphileBuilds',{}) + gc=d.get('graphileCache',{}) + print(f' Builds: {gb.get(\"succeeded\",\"?\")}, Cache: {gc.get(\"size\",\"?\")}') +except: print(' (could not parse memory)') +" + + kill_server +} + +echo "╔══════════════════════════════════════════════════════════════════╗" +echo "║ MULTI-TENANCY CACHE STRESS TEST SUITE ║" +echo "╚══════════════════════════════════════════════════════════════════╝" + +# ─── Test 1: High-K scale (K=100, 4 schema variants, 10 workers, 5min) ────── +run_test "test1-highk" "old" "2000" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=100 DURATION=300 WORKERS=10 + +run_test "test1-highk" "new" "" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=100 DURATION=300 WORKERS=10 + +# ─── Test 2: High concurrency (K=30, 4 schema variants, 10 workers, 5min) ─── +run_test "test2-highconc" "old" "200" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=300 WORKERS=10 + +run_test "test2-highconc" "new" "" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=300 WORKERS=10 + +# ─── Test 3: Flush under load (K=30, 4 variants, 10 workers, flush/30s) ───── +run_test "test3-chaos" "old" "200" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=300 WORKERS=10 CHAOS_FLUSH=true FLUSH_INTERVAL=30 + +run_test "test3-chaos" "new" "" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=300 WORKERS=10 CHAOS_FLUSH=true FLUSH_INTERVAL=30 + +# ─── Test 4: Mixed buildKeys max divergence (K=30, 8 variants, 10 workers) ── +run_test "test4-mixed" "old" "800" \ + SCHEMA_VARIANTS=8 K=30 DURATION=300 WORKERS=10 + +run_test "test4-mixed" "new" "" \ + SCHEMA_VARIANTS=8 K=30 DURATION=300 WORKERS=10 + +# ─── Test 5: Soak (K=30, 4 variants, 10 workers, 2hr, flush/60s) ──────────── +run_test "test5-soak" "old" "200" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=7200 WORKERS=10 CHAOS_FLUSH=true FLUSH_INTERVAL=60 + +run_test "test5-soak" "new" "" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=7200 WORKERS=10 CHAOS_FLUSH=true FLUSH_INTERVAL=60 + +# ─── Test 6: Startup burst (K=30, 4 variants, 10 workers, concurrent cold) ── +run_test "test6-burst" "old" "200" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=60 WORKERS=10 BURST_START=true + +run_test "test6-burst" "new" "" \ + MULTI_ENDPOINT=true SCHEMA_VARIANTS=4 K=30 DURATION=60 WORKERS=10 BURST_START=true + +echo "" +echo "╔══════════════════════════════════════════════════════════════════╗" +echo "║ ALL TESTS COMPLETE ║" +echo "╚══════════════════════════════════════════════════════════════════╝" +echo "" +echo "Results in /tmp/e2e-benchmark-*.json" diff --git a/graphql/server/perf/src/cli.ts b/graphql/server/perf/src/cli.ts new file mode 100644 index 0000000000..f62f5c2ed4 --- /dev/null +++ b/graphql/server/perf/src/cli.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env ts-node +import { commands } from './commands'; + +commands(process.argv.slice(2)).catch((error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exit(1); +}); diff --git a/graphql/server/perf/src/commands.ts b/graphql/server/perf/src/commands.ts new file mode 100644 index 0000000000..d6ef420561 --- /dev/null +++ b/graphql/server/perf/src/commands.ts @@ -0,0 +1,65 @@ +import { e2eMatrix } from './commands/e2e-matrix'; +import { preparePublicAccess } from './commands/prepare-public-access'; +import { privateBenchmark } from './commands/private-benchmark'; +import { privateCompare } from './commands/private-compare'; +import { publicLoad } from './commands/public-load'; +import { publicPreflight } from './commands/public-preflight'; +import { resetBusinessData } from './commands/reset-business-data'; +import { run } from './commands/run'; +import { stress } from './commands/stress'; +import { summarizeShapes } from './commands/summarize-shapes'; +import { sweep } from './commands/sweep'; +import { getPerfPaths } from './lib/paths'; +import { createCommandUsageText, createUsageText } from './utils'; +import type { CommandDefinition, PerfCliOptions } from './types'; + +export const commandDefinitions: CommandDefinition[] = [ + { name: 'private-benchmark', summary: 'Run lightweight private/header-routing HTTP benchmark', run: privateBenchmark }, + { name: 'private-compare', summary: 'Run old/new private-routing comparison', run: privateCompare }, + { name: 'stress', summary: 'Run curated private comparison stress matrix', run: stress }, + { name: 'public-preflight', summary: 'Provision/validate DBPM tenants and generate profiles', run: publicPreflight }, + { name: 'public-load', summary: 'Run DBPM-backed business load', run: publicLoad }, + { name: 'run', summary: 'Run public preflight + load for one run directory', run }, + { name: 'sweep', summary: 'Run repeated K/tenant-count sweeps', run: sweep }, + { name: 'summarize-shapes', summary: 'Summarize DBPM shape variants', run: summarizeShapes }, + { name: 'prepare-public-access', summary: 'Prepare public grants for perf business tables', run: preparePublicAccess }, + { name: 'reset-business-data', summary: 'Truncate generated business workload table data', run: resetBusinessData }, + { name: 'e2e-matrix', summary: 'Run public/private x old/new E2E matrix wrapper', run: e2eMatrix }, +]; + +export function createCommandMap(): Map { + return new Map(commandDefinitions.map((command) => [command.name, command])); +} + +export async function commands(argv: string[], options: PerfCliOptions = {}): Promise { + const [maybeCommand, ...rest] = argv; + const usageText = createUsageText(commandDefinitions); + const stdout = options.stdout ?? process.stdout; + const stderr = options.stderr ?? process.stderr; + + if (!maybeCommand || maybeCommand === '--help' || maybeCommand === '-h' || maybeCommand === 'help') { + stdout.write(`${usageText}\n`); + return; + } + + const command = createCommandMap().get(maybeCommand); + if (!command) { + stderr.write(`Unknown perf command: ${maybeCommand}\n\n`); + stdout.write(`${usageText}\n`); + process.exitCode = 1; + return; + } + + if (rest.includes('--help') || rest.includes('-h')) { + stdout.write(`${createCommandUsageText(command)}\n`); + return; + } + + const dryRun = rest.includes('--dry-run'); + const args = rest.filter((arg) => arg !== '--dry-run'); + await command.run({ + args, + dryRun, + paths: options.paths ?? getPerfPaths(), + }); +} diff --git a/graphql/server/perf/src/commands/e2e-matrix.ts b/graphql/server/perf/src/commands/e2e-matrix.ts new file mode 100644 index 0000000000..05952f72de --- /dev/null +++ b/graphql/server/perf/src/commands/e2e-matrix.ts @@ -0,0 +1,625 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { spawn } from 'node:child_process'; +import { createRequire } from 'node:module'; +import { getNumberFlag, getStringFlag, hasFlag, parseArgs, parseCsv } from '../lib/args'; +import { serverEnv, withLocalhostNoProxy, redactEnv } from '../lib/config'; +import { getJson, waitForJsonOk } from '../lib/http'; +import { defaultRunDir, ensureRunDirs, writeJson } from '../lib/run-dir'; +import { runProcess } from '../lib/process'; +import { nowIso, summarizeError } from '../lib/reports'; +import type { + CacheMode, + CommandContext, + E2eMatrixCaseResult, + E2eMatrixResetResult, + RoutingMode, +} from '../types'; + +type ServerHandle = { + stop: () => Promise; +}; + +type MatrixDirs = Awaited>; + +type JsonRecord = Record; + +function asRoutingMode(value: string): RoutingMode { + if (value === 'private' || value === 'public') return value; + throw new Error(`Invalid routing mode ${value}; expected private|public`); +} + +function asCacheMode(value: string): CacheMode { + if (value === 'old' || value === 'new') return value; + throw new Error(`Invalid cache mode ${value}; expected old|new`); +} + +function isRecord(value: unknown): value is JsonRecord { + return value != null && typeof value === 'object' && !Array.isArray(value); +} + +function getRecord(value: unknown, key: string): JsonRecord | undefined { + if (!isRecord(value)) return undefined; + const child = value[key]; + return isRecord(child) ? child : undefined; +} + +function getNumber(value: unknown): number | undefined { + return typeof value === 'number' && Number.isFinite(value) ? value : undefined; +} + +function getString(value: unknown): string | undefined { + return typeof value === 'string' && value.length > 0 ? value : undefined; +} + +async function pathExists(filePath: string): Promise { + try { + await fs.access(filePath); + return true; + } catch { + return false; + } +} + +async function readJsonFile(filePath: string): Promise { + const raw = await fs.readFile(filePath, 'utf8'); + return JSON.parse(raw) as unknown; +} + +async function removeIfExists(filePath: string): Promise { + try { + await fs.rm(filePath, { force: true }); + } catch { + // Best-effort stale artifact cleanup only. + } +} + +function pushGateFailure(result: E2eMatrixCaseResult, message: string): void { + result.hardGateFailures = [...(result.hardGateFailures ?? []), message]; +} + +async function startManagedServer({ + ctx, + routingMode, + cacheMode, + port, + k, + logsDir, +}: { + ctx: CommandContext; + routingMode: RoutingMode; + cacheMode: CacheMode; + port: number; + k: number; + logsDir: string; +}): Promise { + const env = serverEnv({ routingMode, cacheMode, port, k }); + await fs.mkdir(logsDir, { recursive: true }); + const logPath = path.join(logsDir, `server-${routingMode}-${cacheMode}.log`); + console.log(`[e2e-matrix] starting managed server ${routingMode}/${cacheMode}`); + console.log(`[e2e-matrix] env ${JSON.stringify(redactEnv(env))}`); + console.log(`[e2e-matrix] server log ${logPath}`); + + if (ctx.dryRun) { + return { stop: async () => undefined }; + } + + const log = await fs.open(logPath, 'a'); + const requireFromServer = createRequire(path.join(ctx.paths.serverDir, 'package.json')); + const tsNodeBin = requireFromServer.resolve('ts-node/dist/bin.js'); + const workspaceResolver = path.join(ctx.paths.perfDir, 'src', 'register-workspace-packages.cjs'); + const child = spawn(process.execPath, ['-r', workspaceResolver, tsNodeBin, 'src/run.ts'], { + cwd: ctx.paths.serverDir, + env, + stdio: ['ignore', log.fd, log.fd], + detached: true, + }); + + let stopped = false; + const killChildTree = (signal: NodeJS.Signals) => { + if (!child.pid) return; + try { + process.kill(-child.pid, signal); + } catch { + try { + child.kill(signal); + } catch { + // Already gone. + } + } + }; + const stop = async () => { + if (stopped) return; + stopped = true; + killChildTree('SIGTERM'); + await new Promise((resolve) => { + const timeout = setTimeout(() => { + killChildTree('SIGKILL'); + resolve(); + }, 10_000); + child.once('exit', () => { + clearTimeout(timeout); + resolve(); + }); + }); + await log.close(); + }; + + child.on('exit', (code, signal) => { + if (!stopped && code !== 0) { + console.error(`[e2e-matrix] managed server exited code=${code} signal=${signal ?? ''}; see ${logPath}`); + } + }); + + try { + await waitForJsonOk(`http://localhost:${port}/debug/memory`, 90_000, 1_000); + } catch (error) { + await stop(); + throw error; + } + + return { stop }; +} + +async function captureMemory(baseUrl: string, outputPath: string, dryRun: boolean): Promise { + console.log(`[e2e-matrix] capture memory ${outputPath}`); + if (dryRun) { + return true; + } + const payload = await getJson(`${baseUrl}/debug/memory`, 15_000); + await writeJson(outputPath, payload); + return payload.ok; +} + +async function runPrivateCase({ + ctx, + cacheMode, + k, + durationSeconds, + workers, + port, + dirs, +}: { + ctx: CommandContext; + cacheMode: CacheMode; + k: number; + durationSeconds: number; + workers: number; + port: number; + dirs: MatrixDirs; +}): Promise { + const tmpResultPath = `/tmp/e2e-benchmark-${cacheMode}-k${k}.json`; + const legacyResultPath = path.join(ctx.paths.perfDir, 'results', `e2e-benchmark-${cacheMode}-k${k}.json`); + const matrixResultPath = path.join(dirs.reportsDir, `private-${cacheMode}-result.json`); + + if (!ctx.dryRun) { + await Promise.all([removeIfExists(tmpResultPath), removeIfExists(legacyResultPath), removeIfExists(matrixResultPath)]); + } + + const env = withLocalhostNoProxy({ + ...process.env, + MODE: cacheMode, + K: String(k), + DURATION: String(durationSeconds), + WORKERS: String(workers), + SERVER_PORT: String(port), + API_IS_PUBLIC: 'false', + }); + await runProcess('npx', ['ts-node', path.join(ctx.paths.perfDir, 'e2e-benchmark.ts')], { + cwd: ctx.paths.serverDir, + env, + dryRun: ctx.dryRun, + label: `private-${cacheMode}`, + }); + + if (ctx.dryRun) { + return matrixResultPath; + } + + const sourcePath = (await pathExists(tmpResultPath)) ? tmpResultPath : legacyResultPath; + if (!(await pathExists(sourcePath))) { + throw new Error(`private ${cacheMode} result report not found; checked ${tmpResultPath} and ${legacyResultPath}`); + } + + await fs.mkdir(path.dirname(matrixResultPath), { recursive: true }); + await fs.copyFile(sourcePath, matrixResultPath); + return matrixResultPath; +} + +async function runPublicCase({ + ctx, + cacheMode, + runDir, + baseUrl, + durationSeconds, + workers, + minTenantCount, +}: { + ctx: CommandContext; + cacheMode: CacheMode; + runDir: string; + baseUrl: string; + durationSeconds: number; + workers: number; + minTenantCount: number; +}): Promise { + const tier = `public-${cacheMode}-k${minTenantCount}-${durationSeconds}s`; + const resultPath = path.join(runDir, 'data', `load-${tier}.json`); + if (!ctx.dryRun) { + await removeIfExists(resultPath); + } + await runProcess( + 'npx', + [ + 'ts-node', + path.join(ctx.paths.perfDir, 'src', 'legacy', 'phase2-load.ts'), + '--run-dir', + runDir, + '--base-url', + baseUrl, + '--profiles', + path.join(runDir, 'data', 'business-op-profiles.json'), + '--workers', + String(workers), + '--duration-seconds', + String(durationSeconds), + '--idle-seconds', + '0', + '--min-tenant-count', + String(minTenantCount), + '--public-role', + 'anonymous', + '--tier', + tier, + ], + { + cwd: ctx.paths.repoRoot, + env: withLocalhostNoProxy(process.env), + dryRun: ctx.dryRun, + label: `public-${cacheMode}`, + }, + ); + return resultPath; +} + +async function resetPublicBusinessData({ + ctx, + runDir, + before, + after, +}: { + ctx: CommandContext; + runDir: string; + before: string; + after: string; +}): Promise { + const tag = `between-${before}-and-${after}`.replace(/[^a-zA-Z0-9_-]+/g, '-'); + const reportPath = path.join(runDir, 'reports', `reset-business-test-data-${tag}.json`); + + try { + await runProcess( + 'npx', + [ + 'ts-node', + path.join(ctx.paths.perfDir, 'src', 'cli.ts'), + 'reset-business-data', + '--run-dir', + runDir, + '--profiles', + path.join(runDir, 'data', 'business-op-profiles.json'), + '--public-role', + 'anonymous', + '--tag', + tag, + ], + { + cwd: ctx.paths.serverDir, + env: withLocalhostNoProxy(process.env), + dryRun: ctx.dryRun, + label: `reset-${tag}`, + }, + ); + + if (ctx.dryRun) { + return { before, after, ok: true, reportPath, failureCount: 0 }; + } + + const payload = await readJsonFile(reportPath); + const totals = getRecord(payload, 'totals'); + const failureCount = getNumber(totals?.failureCount) ?? 0; + return { before, after, ok: failureCount === 0, reportPath, failureCount }; + } catch (error) { + return { before, after, ok: false, reportPath, error: summarizeError(error) }; + } +} + +async function provisionPublicIfRequested({ + ctx, + runDir, + baseUrl, + k, + keyspaceMinRouteKeys, + skipPublicPreflight, +}: { + ctx: CommandContext; + runDir: string; + baseUrl: string; + k: number; + keyspaceMinRouteKeys: number; + skipPublicPreflight: boolean; +}): Promise { + if (skipPublicPreflight) { + console.log('[e2e-matrix] skipping public preflight; expecting existing business-op-profiles.json'); + return; + } + await runProcess( + 'npx', + [ + 'ts-node', + path.join(ctx.paths.perfDir, 'src', 'legacy', 'phase1-preflight.ts'), + '--run-dir', + runDir, + '--base-url', + baseUrl, + '--dbpm-tenant-count', + String(k), + '--min-tenant-count', + String(k), + '--dbpm-shape-variants', + '1', + '--auth-host', + 'auth.localhost', + '--provision-host', + 'modules.localhost', + '--business-routing-mode', + 'public', + '--business-compat-routing-mode', + 'public', + '--business-public-api-name', + 'api', + '--business-public-subdomain-prefix', + 'api-dbpm-', + '--min-token-tenants', + String(k), + '--keyspace-min-route-keys', + String(keyspaceMinRouteKeys), + ], + { + cwd: ctx.paths.repoRoot, + env: withLocalhostNoProxy(process.env), + dryRun: ctx.dryRun, + label: 'public-preflight', + }, + ); +} + +async function summarizePrivateResult(result: E2eMatrixCaseResult, resultPath: string, dryRun: boolean): Promise { + result.resultPath = resultPath; + if (dryRun) { + result.reportExists = true; + result.errors = 0; + result.failed = 0; + return; + } + + result.reportExists = await pathExists(resultPath); + if (!result.reportExists) { + pushGateFailure(result, `result report missing: ${resultPath}`); + return; + } + + const payload = await readJsonFile(resultPath); + if (!isRecord(payload)) { + pushGateFailure(result, `result report is not an object: ${resultPath}`); + return; + } + + const errors = getNumber(payload.errors); + if (errors == null) { + pushGateFailure(result, `private benchmark report missing numeric errors field: ${resultPath}`); + return; + } + result.errors = errors; + result.failed = errors; + result.totalRequests = getNumber(payload.totalQueries); + result.qps = getNumber(payload.qps); + result.p95Ms = getNumber(payload.p95) ?? null; + result.p99Ms = getNumber(payload.p99) ?? null; + result.heapDeltaMb = getNumber(payload.heapDelta); + + if (errors !== 0) { + pushGateFailure(result, `private benchmark errors=${errors}`); + } +} + +async function summarizePublicResult(result: E2eMatrixCaseResult, resultPath: string, dryRun: boolean): Promise { + result.resultPath = resultPath; + if (dryRun) { + result.reportExists = true; + result.errors = 0; + result.failed = 0; + return; + } + + result.reportExists = await pathExists(resultPath); + if (!result.reportExists) { + pushGateFailure(result, `result report missing: ${resultPath}`); + return; + } + + const payload = await readJsonFile(resultPath); + if (!isRecord(payload)) { + pushGateFailure(result, `result report is not an object: ${resultPath}`); + return; + } + + const load = getRecord(payload, 'load'); + if (!load) { + pushGateFailure(result, `public load report missing load object: ${resultPath}`); + return; + } + const latency = getRecord(load, 'latencyMs'); + const failed = getNumber(load.failed); + if (failed == null) { + pushGateFailure(result, `public load report missing numeric load.failed field: ${resultPath}`); + return; + } + + result.failed = failed; + result.errors = failed; + result.totalRequests = getNumber(load?.total); + result.qps = getNumber(load?.requestsPerSecond); + result.p95Ms = getNumber(latency?.p95) ?? null; + result.p99Ms = getNumber(latency?.p99) ?? null; + + if (failed !== 0) { + pushGateFailure(result, `public load failed=${failed}`); + } + + const routeProbe = getRecord(payload, 'routeProbe'); + if (routeProbe && routeProbe.ok === false) { + pushGateFailure(result, 'public routeProbe.ok=false'); + } + + const prewarm = getRecord(payload, 'prewarm'); + const prewarmFailed = getNumber(prewarm?.failed) ?? 0; + if (prewarmFailed !== 0) { + pushGateFailure(result, `public prewarm failed=${prewarmFailed}`); + } +} + +function shouldResetAfterPublicCase({ + routingModes, + cacheModes, + routingMode, + cacheMode, + resetBetweenPublicCacheModes, +}: { + routingModes: RoutingMode[]; + cacheModes: CacheMode[]; + routingMode: RoutingMode; + cacheMode: CacheMode; + resetBetweenPublicCacheModes: boolean; +}): boolean { + if (!resetBetweenPublicCacheModes || routingMode !== 'public' || !routingModes.includes('public')) { + return false; + } + return cacheModes.indexOf(cacheMode) < cacheModes.length - 1; +} + +export async function e2eMatrix(ctx: CommandContext): Promise { + const parsed = parseArgs(ctx.args); + const routingModes = parseCsv(getStringFlag(parsed.flags, '--routing-modes'), ['private', 'public']).map(asRoutingMode); + const cacheModes = parseCsv(getStringFlag(parsed.flags, '--cache-modes'), ['old', 'new']).map(asCacheMode); + const k = getNumberFlag(parsed.flags, '--k', 10); + const durationSeconds = getNumberFlag(parsed.flags, '--duration-seconds', 300); + const workers = getNumberFlag(parsed.flags, '--workers', 4); + const port = getNumberFlag(parsed.flags, '--port', 3000); + const keyspaceMinRouteKeys = getNumberFlag(parsed.flags, '--keyspace-min-route-keys', k); + const baseUrl = getStringFlag(parsed.flags, '--base-url') || `http://localhost:${port}`; + const runDir = path.resolve(getStringFlag(parsed.flags, '--run-dir', defaultRunDir('e2e-matrix')) || defaultRunDir('e2e-matrix')); + const manageServer = hasFlag(parsed.flags, '--manage-server'); + const skipPublicPreflight = hasFlag(parsed.flags, '--skip-public-preflight'); + const resetBetweenPublicCacheModes = !hasFlag(parsed.flags, '--no-reset-between-public-cache-modes'); + const dirs = await ensureRunDirs(runDir); + const results: E2eMatrixCaseResult[] = []; + const runOrder: string[] = []; + + const config = { + routingModes, + cacheModes, + k, + durationSeconds, + workers, + port, + keyspaceMinRouteKeys, + baseUrl, + runDir, + manageServer, + skipPublicPreflight, + resetBetweenPublicCacheModes, + }; + console.log('[e2e-matrix] config', JSON.stringify(config, null, 2)); + + if (routingModes.includes('public')) { + if (manageServer) { + const server = await startManagedServer({ ctx, routingMode: 'public', cacheMode: 'new', port, k, logsDir: dirs.logsDir }); + try { + await provisionPublicIfRequested({ ctx, runDir, baseUrl, k, keyspaceMinRouteKeys, skipPublicPreflight }); + } finally { + await server.stop(); + } + } else { + await provisionPublicIfRequested({ ctx, runDir, baseUrl, k, keyspaceMinRouteKeys, skipPublicPreflight }); + } + } + + for (const routingMode of routingModes) { + for (const cacheMode of cacheModes) { + const startedAt = nowIso(); + const result: E2eMatrixCaseResult = { routingMode, cacheMode, ok: false, startedAt, finishedAt: startedAt }; + runOrder.push(`${routingMode}/${cacheMode}`); + let server: ServerHandle | undefined; + try { + if (manageServer) { + server = await startManagedServer({ ctx, routingMode, cacheMode, port, k, logsDir: dirs.logsDir }); + } + const memoryBeforePath = path.join(dirs.reportsDir, `memory-${routingMode}-${cacheMode}-before.json`); + const memoryAfterPath = path.join(dirs.reportsDir, `memory-${routingMode}-${cacheMode}-after.json`); + result.memoryBeforePath = memoryBeforePath; + result.memoryBeforeOk = await captureMemory(baseUrl, memoryBeforePath, ctx.dryRun); + if (!result.memoryBeforeOk) { + pushGateFailure(result, `memory before capture failed: ${memoryBeforePath}`); + } + + if (routingMode === 'private') { + const resultPath = await runPrivateCase({ ctx, cacheMode, k, durationSeconds, workers, port, dirs }); + await summarizePrivateResult(result, resultPath, ctx.dryRun); + } else { + const resultPath = await runPublicCase({ ctx, cacheMode, runDir, baseUrl, durationSeconds, workers, minTenantCount: k }); + await summarizePublicResult(result, resultPath, ctx.dryRun); + } + + result.memoryAfterPath = memoryAfterPath; + result.memoryAfterOk = await captureMemory(baseUrl, memoryAfterPath, ctx.dryRun); + if (!result.memoryAfterOk) { + pushGateFailure(result, `memory after capture failed: ${memoryAfterPath}`); + } + } catch (error) { + result.error = summarizeError(error); + pushGateFailure(result, result.error); + console.error(`[e2e-matrix] case failed ${routingMode}/${cacheMode}: ${result.error}`); + } finally { + if (server) await server.stop(); + + if (shouldResetAfterPublicCase({ routingModes, cacheModes, routingMode, cacheMode, resetBetweenPublicCacheModes })) { + const nextCacheMode = cacheModes[cacheModes.indexOf(cacheMode) + 1]; + const reset = await resetPublicBusinessData({ + ctx, + runDir, + before: `public/${cacheMode}`, + after: `public/${nextCacheMode}`, + }); + result.resetAfter = reset; + if (!reset.ok) { + pushGateFailure(result, reset.error ? `reset failed: ${reset.error}` : `reset failed: failureCount=${reset.failureCount ?? 'unknown'}`); + } + } + + result.ok = (result.hardGateFailures ?? []).length === 0; + result.finishedAt = nowIso(); + results.push(result); + await writeJson(path.join(dirs.reportsDir, 'e2e-matrix-summary.json'), { + matrix: config, + runOrder, + results, + pass: results.every((item) => item.ok), + }); + } + } + } + + const pass = results.every((item) => item.ok); + console.log(JSON.stringify({ runDir, pass, runOrder, results }, null, 2)); + if (!pass) process.exitCode = 1; +} diff --git a/graphql/server/perf/src/commands/legacy.ts b/graphql/server/perf/src/commands/legacy.ts new file mode 100644 index 0000000000..a67e163034 --- /dev/null +++ b/graphql/server/perf/src/commands/legacy.ts @@ -0,0 +1,38 @@ +import path from 'node:path'; +import { mapAliases } from '../lib/args'; +import { runProcess } from '../lib/process'; +import type { CommandContext } from '../types'; + +export async function runMigratedTsScript( + ctx: CommandContext, + scriptName: string, + args = ctx.args, +): Promise { + await runProcess('npx', ['ts-node', path.join(ctx.paths.perfDir, 'src', 'legacy', scriptName), ...args], { + cwd: ctx.paths.repoRoot, + env: process.env, + dryRun: ctx.dryRun, + label: `src/legacy/${scriptName}`, + }); +} + +export async function runShellLegacyScript( + ctx: CommandContext, + scriptName: string, + args = ctx.args, +): Promise { + await runProcess('bash', [path.join(ctx.paths.perfDir, scriptName), ...args], { + cwd: ctx.paths.repoRoot, + env: process.env, + dryRun: ctx.dryRun, + label: scriptName, + }); +} + +export function normalizeDurationAlias(args: string[]): string[] { + return mapAliases(args, { + '--duration-seconds': '--duration', + '--idle-seconds': '--idle', + '--server-port': '--port', + }); +} diff --git a/graphql/server/perf/src/commands/prepare-public-access.ts b/graphql/server/perf/src/commands/prepare-public-access.ts new file mode 100644 index 0000000000..6c78d1e545 --- /dev/null +++ b/graphql/server/perf/src/commands/prepare-public-access.ts @@ -0,0 +1,101 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { getStringFlag, hasFlag, parseArgs } from '../lib/args'; +import { pgConfigFromEnv } from '../lib/pg'; +import { defaultRunDir, ensureRunDirs, writeJson } from '../lib/run-dir'; +import { + buildTargetsFromProfiles, + ensurePublicAccessForTargets, + extractProfiles, + getUnsafeTargets, +} from '../lib/public-access'; +import type { CommandContext } from '../types'; + +async function readJson(filePath: string): Promise { + const raw = await fs.readFile(filePath, 'utf8'); + return JSON.parse(raw); +} + +export async function preparePublicAccess(ctx: CommandContext): Promise { + const parsed = parseArgs(ctx.args); + const runDir = path.resolve(getStringFlag(parsed.flags, '--run-dir', defaultRunDir('graphile-cache-public-access')) || defaultRunDir('graphile-cache-public-access')); + const profilesPath = path.resolve( + getStringFlag(parsed.flags, '--profiles', path.join(runDir, 'data', 'business-op-profiles.public.json')) || + path.join(runDir, 'data', 'business-op-profiles.public.json'), + ); + const allowNonPerfSchema = hasFlag(parsed.flags, '--allow-non-perf-schema'); + const tag = (getStringFlag(parsed.flags, '--tag', '') || '').trim(); + const publicRole = (getStringFlag(parsed.flags, '--public-role', 'authenticated') || 'authenticated').trim(); + const publicReadRole = (getStringFlag(parsed.flags, '--public-read-role', 'anonymous') || 'anonymous').trim(); + + if (!publicRole) { + throw new Error('--public-role cannot be empty'); + } + + const pgConfig = { + ...pgConfigFromEnv(), + host: getStringFlag(parsed.flags, '--pg-host') || process.env.PGHOST || 'localhost', + port: Number.parseInt(getStringFlag(parsed.flags, '--pg-port') || process.env.PGPORT || '5432', 10), + database: getStringFlag(parsed.flags, '--pg-database') || process.env.PGDATABASE || 'constructive', + user: getStringFlag(parsed.flags, '--pg-user') || process.env.PGUSER || 'postgres', + password: getStringFlag(parsed.flags, '--pg-password') || process.env.PGPASSWORD || 'password', + }; + + const dirs = await ensureRunDirs(runDir); + const reportName = tag ? `prepare-public-test-access-${tag}.json` : 'prepare-public-test-access.json'; + const reportPath = path.join(dirs.reportsDir, reportName); + + const profilesPayload = await readJson(profilesPath); + const profiles = extractProfiles(profilesPayload); + if (profiles.length === 0) { + throw new Error(`No profiles found in ${profilesPath}`); + } + + const targets = buildTargetsFromProfiles(profiles); + if (targets.length === 0) { + throw new Error(`No table targets found in ${profilesPath}`); + } + + const unsafeTargets = getUnsafeTargets(targets); + if (unsafeTargets.length > 0 && !allowNonPerfSchema) { + throw new Error( + `Refusing to prepare non-perf schemas: ${unsafeTargets + .map((target) => `${target.schemaName}.${target.tableName}`) + .join(', ')}`, + ); + } + + const preparedResult = await ensurePublicAccessForTargets({ + targets, + pgConfig, + dryRun: ctx.dryRun, + publicRole, + publicReadRole, + }); + + const report = { + createdAt: new Date().toISOString(), + runDir, + profilesPath, + options: { + dryRun: ctx.dryRun, + allowNonPerfSchema, + publicRole, + publicReadRole: publicReadRole || null, + tag: tag || null, + }, + totals: { + profileCount: profiles.length, + targetCount: targets.length, + preparedCount: preparedResult.prepared.length, + failureCount: preparedResult.failures.length, + }, + targets, + prepared: preparedResult.prepared, + failures: preparedResult.failures, + }; + + await writeJson(reportPath, report); + console.log(JSON.stringify({ reportPath, targetCount: targets.length, preparedCount: preparedResult.prepared.length, failureCount: preparedResult.failures.length }, null, 2)); + if (preparedResult.failures.length > 0) process.exitCode = 1; +} diff --git a/graphql/server/perf/src/commands/private-benchmark.ts b/graphql/server/perf/src/commands/private-benchmark.ts new file mode 100644 index 0000000000..cb5ae8d342 --- /dev/null +++ b/graphql/server/perf/src/commands/private-benchmark.ts @@ -0,0 +1,41 @@ +import path from 'node:path'; +import { getNumberFlag, getStringFlag, parseArgs } from '../lib/args'; +import { withLocalhostNoProxy } from '../lib/config'; +import { runProcess } from '../lib/process'; +import type { CacheMode, CommandContext } from '../types'; + +export async function privateBenchmark(ctx: CommandContext): Promise { + const parsed = parseArgs(ctx.args); + const mode = (getStringFlag(parsed.flags, '--mode', process.env.MODE || 'new') || 'new') as CacheMode; + if (mode !== 'old' && mode !== 'new') { + throw new Error(`Invalid --mode=${mode}; expected old|new`); + } + const k = getNumberFlag(parsed.flags, '--k', Number(process.env.K || 30)); + const duration = getNumberFlag( + parsed.flags, + '--duration-seconds', + getNumberFlag(parsed.flags, '--duration', Number(process.env.DURATION || 300)), + ); + const workers = getNumberFlag(parsed.flags, '--workers', Number(process.env.WORKERS || 8)); + const port = getNumberFlag( + parsed.flags, + '--server-port', + getNumberFlag(parsed.flags, '--port', Number(process.env.SERVER_PORT || 3000)), + ); + + const env = withLocalhostNoProxy({ + ...process.env, + MODE: mode, + K: String(k), + DURATION: String(duration), + WORKERS: String(workers), + SERVER_PORT: String(port), + }); + + await runProcess('npx', ['ts-node', path.join(ctx.paths.perfDir, 'e2e-benchmark.ts')], { + cwd: ctx.paths.serverDir, + env, + dryRun: ctx.dryRun, + label: 'private-benchmark', + }); +} diff --git a/graphql/server/perf/src/commands/private-compare.ts b/graphql/server/perf/src/commands/private-compare.ts new file mode 100644 index 0000000000..ce06dd6864 --- /dev/null +++ b/graphql/server/perf/src/commands/private-compare.ts @@ -0,0 +1,6 @@ +import { normalizeDurationAlias, runShellLegacyScript } from './legacy'; +import type { CommandContext } from '../types'; + +export async function privateCompare(ctx: CommandContext): Promise { + await runShellLegacyScript(ctx, 'run-comparison.sh', normalizeDurationAlias(ctx.args)); +} diff --git a/graphql/server/perf/src/commands/public-load.ts b/graphql/server/perf/src/commands/public-load.ts new file mode 100644 index 0000000000..21e41561eb --- /dev/null +++ b/graphql/server/perf/src/commands/public-load.ts @@ -0,0 +1,6 @@ +import { runMigratedTsScript } from './legacy'; +import type { CommandContext } from '../types'; + +export async function publicLoad(ctx: CommandContext): Promise { + await runMigratedTsScript(ctx, 'phase2-load.ts'); +} diff --git a/graphql/server/perf/src/commands/public-preflight.ts b/graphql/server/perf/src/commands/public-preflight.ts new file mode 100644 index 0000000000..defa6fc636 --- /dev/null +++ b/graphql/server/perf/src/commands/public-preflight.ts @@ -0,0 +1,6 @@ +import { runMigratedTsScript } from './legacy'; +import type { CommandContext } from '../types'; + +export async function publicPreflight(ctx: CommandContext): Promise { + await runMigratedTsScript(ctx, 'phase1-preflight.ts'); +} diff --git a/graphql/server/perf/src/commands/reset-business-data.ts b/graphql/server/perf/src/commands/reset-business-data.ts new file mode 100644 index 0000000000..274f1d3a9a --- /dev/null +++ b/graphql/server/perf/src/commands/reset-business-data.ts @@ -0,0 +1,156 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { Pool } from 'pg'; +import { getStringFlag, hasFlag, parseArgs } from '../lib/args'; +import { pgConfigFromEnv } from '../lib/pg'; +import { defaultRunDir, ensureRunDirs, writeJson } from '../lib/run-dir'; +import { + buildTargetsFromProfiles, + ensurePublicAccessForTargets, + extractProfiles, + getUnsafeTargets, + quoteIdent, + type PublicAccessFailure, +} from '../lib/public-access'; +import type { CommandContext } from '../types'; + +async function readJson(filePath: string): Promise { + const raw = await fs.readFile(filePath, 'utf8'); + return JSON.parse(raw); +} + +export async function resetBusinessData(ctx: CommandContext): Promise { + const parsed = parseArgs(ctx.args); + const runDir = path.resolve(getStringFlag(parsed.flags, '--run-dir', defaultRunDir('graphile-cache-reset')) || defaultRunDir('graphile-cache-reset')); + const profilesPath = path.resolve( + getStringFlag(parsed.flags, '--profiles', path.join(runDir, 'data', 'business-op-profiles.json')) || + path.join(runDir, 'data', 'business-op-profiles.json'), + ); + const allowNonPerfSchema = hasFlag(parsed.flags, '--allow-non-perf-schema'); + const ensurePublicTestAccess = hasFlag(parsed.flags, '--ensure-public-test-access'); + const publicRole = getStringFlag(parsed.flags, '--public-role', 'authenticated') || 'authenticated'; + const publicReadRole = getStringFlag(parsed.flags, '--public-read-role', 'anonymous') || 'anonymous'; + const tag = (getStringFlag(parsed.flags, '--tag', '') || '').trim(); + + const pgConfig = { + ...pgConfigFromEnv(), + host: getStringFlag(parsed.flags, '--pg-host') || process.env.PGHOST || 'localhost', + port: Number.parseInt(getStringFlag(parsed.flags, '--pg-port') || process.env.PGPORT || '5432', 10), + database: getStringFlag(parsed.flags, '--pg-database') || process.env.PGDATABASE || 'constructive', + user: getStringFlag(parsed.flags, '--pg-user') || process.env.PGUSER || 'postgres', + password: getStringFlag(parsed.flags, '--pg-password') || process.env.PGPASSWORD || 'password', + }; + + const dirs = await ensureRunDirs(runDir); + const reportName = tag ? `reset-business-test-data-${tag}.json` : 'reset-business-test-data.json'; + const reportPath = path.join(dirs.reportsDir, reportName); + + const profilesPayload = await readJson(profilesPath); + const profiles = extractProfiles(profilesPayload); + if (profiles.length === 0) { + throw new Error(`No business profiles found in ${profilesPath}`); + } + + const targets = buildTargetsFromProfiles(profiles); + if (targets.length === 0) { + throw new Error(`No truncation targets found from profiles in ${profilesPath}`); + } + + const unsafeTargets = getUnsafeTargets(targets); + if (unsafeTargets.length > 0 && !allowNonPerfSchema) { + throw new Error( + `Refusing to truncate non-perf schemas: ${unsafeTargets + .map((target) => `${target.schemaName}.${target.tableName}`) + .join(', ')}`, + ); + } + + const startedAt = new Date().toISOString(); + const truncateFailures: PublicAccessFailure[] = []; + const executed: Array> = []; + const pool = new Pool(pgConfig); + + try { + for (const target of targets) { + const qualified = `${quoteIdent(target.schemaName)}.${quoteIdent(target.tableName)}`; + const sql = `truncate table ${qualified};`; + + if (ctx.dryRun) { + executed.push({ ...target, sql, dryRun: true }); + continue; + } + + try { + await pool.query(sql); + executed.push({ ...target, sql, dryRun: false }); + } catch (error) { + truncateFailures.push({ + ...target, + sql, + phase: 'truncate', + error: error instanceof Error ? error.message : String(error), + }); + } + } + } finally { + await pool.end(); + } + + const accessTargets = ctx.dryRun + ? targets + : executed.map((entry) => ({ + schemaName: String(entry.schemaName), + tableName: String(entry.tableName), + databaseId: (entry.databaseId as string | null | undefined) ?? null, + profileKey: (entry.profileKey as string | null | undefined) ?? null, + })); + const accessResult = ensurePublicTestAccess + ? await ensurePublicAccessForTargets({ + targets: accessTargets, + pgConfig, + dryRun: ctx.dryRun, + publicRole, + publicReadRole, + }) + : { prepared: [], failures: [] }; + + const failures = [...truncateFailures, ...accessResult.failures]; + const report = { + createdAt: new Date().toISOString(), + startedAt, + endedAt: new Date().toISOString(), + runDir, + profilesPath, + pg: { + host: pgConfig.host, + port: pgConfig.port, + database: pgConfig.database, + user: pgConfig.user, + }, + options: { + dryRun: ctx.dryRun, + allowNonPerfSchema, + ensurePublicTestAccess, + publicRole, + publicReadRole: publicReadRole || null, + tag: tag || null, + }, + totals: { + profileCount: profiles.length, + targetCount: targets.length, + truncatedCount: executed.length, + accessPreparedCount: accessResult.prepared.length, + failureCount: failures.length, + }, + targets, + executed, + accessPrepared: accessResult.prepared, + truncateFailures, + accessFailures: accessResult.failures, + failures, + }; + + await writeJson(reportPath, report); + console.log(JSON.stringify({ reportPath, targetCount: targets.length, truncatedCount: executed.length, failureCount: failures.length }, null, 2)); + if (failures.length > 0) process.exitCode = 1; +} diff --git a/graphql/server/perf/src/commands/run.ts b/graphql/server/perf/src/commands/run.ts new file mode 100644 index 0000000000..1aa8203a0c --- /dev/null +++ b/graphql/server/perf/src/commands/run.ts @@ -0,0 +1,28 @@ +import { getStringFlag, parseArgs, stripFlags } from '../lib/args'; +import { publicLoad } from './public-load'; +import { publicPreflight } from './public-preflight'; +import type { CommandContext } from '../types'; + +export async function run(ctx: CommandContext): Promise { + const parsed = parseArgs(ctx.args); + const phase = getStringFlag(parsed.flags, '--phase', 'all') || 'all'; + const args = stripFlags(ctx.args, ['--phase']); + const nextCtx = { ...ctx, args }; + + if (phase === 'phase1') { + await publicPreflight(nextCtx); + return; + } + + if (phase === 'phase2') { + await publicLoad(nextCtx); + return; + } + + if (phase !== 'all') { + throw new Error(`Unknown --phase value: ${phase}`); + } + + await publicPreflight(nextCtx); + await publicLoad(nextCtx); +} diff --git a/graphql/server/perf/src/commands/stress.ts b/graphql/server/perf/src/commands/stress.ts new file mode 100644 index 0000000000..ae5313a294 --- /dev/null +++ b/graphql/server/perf/src/commands/stress.ts @@ -0,0 +1,6 @@ +import { runShellLegacyScript } from './legacy'; +import type { CommandContext } from '../types'; + +export async function stress(ctx: CommandContext): Promise { + await runShellLegacyScript(ctx, 'run-stress-suite.sh'); +} diff --git a/graphql/server/perf/src/commands/summarize-shapes.ts b/graphql/server/perf/src/commands/summarize-shapes.ts new file mode 100644 index 0000000000..ed9c58adfd --- /dev/null +++ b/graphql/server/perf/src/commands/summarize-shapes.ts @@ -0,0 +1,169 @@ +import { createHash } from 'node:crypto'; +import { readFileSync } from 'node:fs'; +import { Pool } from 'pg'; +import { getStringFlag, parseArgs } from '../lib/args'; +import { pgConfigFromEnv } from '../lib/pg'; +import type { CommandContext } from '../types'; + +interface ManifestEntry { + tenantKey?: string; + physicalSchema?: string; +} + +interface ColumnRow { + table_name: string; + column_name: string; + data_type: string; + is_nullable: string; + ordinal_position: number; +} + +interface TableSummary { + columnCount: number; + columns: string[]; +} + +interface ShapeGroup { + tenants: string[]; + tableCount: number; + tableSummaries: TableSummary[]; +} + +function buildTableSignature(columns: ColumnRow[]): string[] { + return [...columns] + .sort((a, b) => Number(a.ordinal_position) - Number(b.ordinal_position)) + .map((column) => `${column.data_type}:${column.is_nullable}`); +} + +function buildSchemaFingerprint(rows: ColumnRow[]): { + fingerprint: string; + tableCount: number; + tableSummaries: TableSummary[]; +} { + const tables = new Map(); + for (const row of rows) { + const existing = tables.get(row.table_name) ?? []; + existing.push(row); + tables.set(row.table_name, existing); + } + + const tableSignatures = [...tables.values()].map((columns) => buildTableSignature(columns)); + tableSignatures.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))); + + const canonical = JSON.stringify(tableSignatures); + const fingerprint = createHash('sha256').update(canonical).digest('hex').slice(0, 16); + + return { + fingerprint, + tableCount: tableSignatures.length, + tableSummaries: tableSignatures.map((signature) => ({ + columnCount: signature.length, + columns: signature, + })), + }; +} + +export async function summarizeShapes(ctx: CommandContext): Promise { + const parsed = parseArgs(ctx.args); + const manifestPath = getStringFlag(parsed.flags, '--manifest'); + if (!manifestPath) { + throw new Error('Usage: perf summarize-shapes --manifest '); + } + + const pgConfig = { + ...pgConfigFromEnv(), + host: getStringFlag(parsed.flags, '--pg-host') || process.env.PGHOST || 'localhost', + port: Number.parseInt(getStringFlag(parsed.flags, '--pg-port') || process.env.PGPORT || '5432', 10), + database: getStringFlag(parsed.flags, '--pg-database') || process.env.PGDATABASE || 'constructive', + user: getStringFlag(parsed.flags, '--pg-user') || process.env.PGUSER || 'postgres', + password: getStringFlag(parsed.flags, '--pg-password') || process.env.PGPASSWORD || 'password', + }; + + if (ctx.dryRun) { + console.log(`[summarize-shapes] would read ${manifestPath} and query ${pgConfig.host}:${pgConfig.port}/${pgConfig.database}`); + return; + } + + const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as ManifestEntry[]; + if (!Array.isArray(manifest) || manifest.length === 0) { + throw new Error('Manifest is empty or not an array'); + } + + const pool = new Pool(pgConfig); + try { + const groups = new Map(); + + for (const entry of manifest) { + const schema = entry.physicalSchema; + const tenantKey = entry.tenantKey || ''; + if (!schema) { + console.warn(`Skipping tenant ${tenantKey}: no physicalSchema`); + continue; + } + + const result = await pool.query( + ` + SELECT table_name, column_name, data_type, is_nullable, ordinal_position + FROM information_schema.columns + WHERE table_schema = $1 + ORDER BY table_name, ordinal_position + `, + [schema], + ); + + if (result.rows.length === 0) { + console.warn(`Skipping tenant ${tenantKey}: no columns found in schema ${schema}`); + continue; + } + + const { fingerprint, tableCount, tableSummaries } = buildSchemaFingerprint(result.rows); + const group = groups.get(fingerprint) ?? { tenants: [], tableCount, tableSummaries }; + group.tenants.push(tenantKey); + groups.set(fingerprint, group); + } + + console.log('\n=== Structural Shape Summary ===\n'); + console.log(`Total tenants analyzed: ${manifest.length}`); + console.log(`Distinct structural groups: ${groups.size}\n`); + + let groupIndex = 0; + for (const [fingerprint, group] of groups) { + groupIndex += 1; + console.log(`--- Group ${groupIndex} (fingerprint: ${fingerprint}) ---`); + console.log(` Tenants: ${group.tenants.length}`); + console.log(` Tables: ${group.tableCount}`); + for (let tableIndex = 0; tableIndex < group.tableSummaries.length; tableIndex += 1) { + const tableSummary = group.tableSummaries[tableIndex]; + console.log(` Table ${tableIndex + 1}: ${tableSummary.columnCount} columns`); + for (const column of tableSummary.columns) { + console.log(` - ${column}`); + } + } + if (group.tenants.length <= 10) { + console.log(` Tenant keys: ${group.tenants.join(', ')}`); + } else { + console.log(` Tenant keys (first 10): ${group.tenants.slice(0, 10).join(', ')} ...`); + } + console.log(''); + } + + const summary = { + totalTenants: manifest.length, + distinctGroups: groups.size, + groups: [...groups.entries()].map(([fingerprint, group]) => ({ + fingerprint, + tenantCount: group.tenants.length, + tableCount: group.tableCount, + columnLayouts: group.tableSummaries.map((tableSummary) => ({ + columnCount: tableSummary.columnCount, + columns: tableSummary.columns, + })), + })), + }; + + console.log('--- JSON Summary ---'); + console.log(JSON.stringify(summary, null, 2)); + } finally { + await pool.end(); + } +} diff --git a/graphql/server/perf/src/commands/sweep.ts b/graphql/server/perf/src/commands/sweep.ts new file mode 100644 index 0000000000..5309140e06 --- /dev/null +++ b/graphql/server/perf/src/commands/sweep.ts @@ -0,0 +1,26 @@ +import path from 'node:path'; +import { getStringFlag, parseArgs, parseCsv, stripFlags } from '../lib/args'; +import { defaultRunDir } from '../lib/run-dir'; +import { e2eMatrix } from './e2e-matrix'; +import type { CommandContext } from '../types'; + +export async function sweep(ctx: CommandContext): Promise { + const parsed = parseArgs(ctx.args); + const kValues = parseCsv(getStringFlag(parsed.flags, '--k-values'), ['3', '7']) + .map((value) => Number.parseInt(value, 10)) + .filter((value) => Number.isFinite(value) && value > 0); + + if (kValues.length === 0) { + throw new Error('No valid values found in --k-values'); + } + + const baseRunDir = path.resolve(getStringFlag(parsed.flags, '--run-dir', defaultRunDir('e2e-sweep')) || defaultRunDir('e2e-sweep')); + const forwardedArgs = stripFlags(ctx.args, ['--k-values', '--k', '--run-dir']); + + for (const k of [...new Set(kValues)]) { + await e2eMatrix({ + ...ctx, + args: [...forwardedArgs, '--k', String(k), '--run-dir', path.join(baseRunDir, `k-${k}`)], + }); + } +} diff --git a/graphql/server/perf/src/legacy/build-business-op-profiles.ts b/graphql/server/perf/src/legacy/build-business-op-profiles.ts new file mode 100644 index 0000000000..0e2dcb36a7 --- /dev/null +++ b/graphql/server/perf/src/legacy/build-business-op-profiles.ts @@ -0,0 +1,484 @@ +#!/usr/bin/env node +// @ts-nocheck + +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { Pool } from 'pg'; + +import { + DEFAULT_BASE_URL, + DEFAULT_TMP_ROOT, + ensureRunDirs, + getArgValue, + makeRunId, + writeJson, +} from './common'; + +const args = process.argv.slice(2); + +const runId = getArgValue(args, '--run-id', makeRunId('business-op-profiles')); +const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId))); +const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL); + +const manifestPath = path.resolve( + getArgValue(args, '--manifest', path.join(runDir, 'data', 'business-table-manifest.json')), +); +const tokensPath = path.resolve( + getArgValue(args, '--tokens', path.join(runDir, 'data', 'tokens.json')), +); + +const routingMode = getArgValue(args, '--routing-mode', 'private').trim().toLowerCase(); +const compatRoutingMode = getArgValue( + args, + '--compat-routing-mode', + routingMode === 'dual' ? 'private' : routingMode, +) + .trim() + .toLowerCase(); +const publicApiName = getArgValue(args, '--public-api-name', 'api').trim(); +const publicSubdomainPrefix = getArgValue(args, '--public-subdomain-prefix', 'api-dbpm-').trim(); + +const outputPath = path.resolve( + getArgValue(args, '--output', path.join(runDir, 'data', 'business-op-profiles.json')), +); +const privateOutputPath = path.resolve( + getArgValue( + args, + '--private-output', + routingMode === 'dual' ? path.join(runDir, 'data', 'business-op-profiles.private.json') : outputPath, + ), +); +const publicOutputPath = path.resolve( + getArgValue( + args, + '--public-output', + routingMode === 'dual' ? path.join(runDir, 'data', 'business-op-profiles.public.json') : outputPath, + ), +); + +const pgConfig = { + host: getArgValue(args, '--pg-host', process.env.PGHOST || 'localhost'), + port: Number.parseInt(getArgValue(args, '--pg-port', process.env.PGPORT || '5432'), 10), + database: getArgValue(args, '--pg-database', process.env.PGDATABASE || 'constructive'), + user: getArgValue(args, '--pg-user', process.env.PGUSER || 'postgres'), + password: getArgValue(args, '--pg-password', process.env.PGPASSWORD || 'password'), +}; + +const VALID_ROUTING_MODES = new Set(['private', 'public', 'dual']); + +const toPascalCase = (value) => + String(value) + .split(/[^a-zA-Z0-9]+/) + .filter(Boolean) + .map((part) => part[0].toUpperCase() + part.slice(1)) + .join(''); + +const toCamelCase = (pascal) => (pascal.length ? pascal[0].toLowerCase() + pascal.slice(1) : pascal); + +const loadJson = async (filePath) => { + const raw = await fs.readFile(filePath, 'utf8'); + return JSON.parse(raw); +}; + +const extractTokenProfiles = (payload) => { + if (Array.isArray(payload)) return payload; + if (Array.isArray(payload?.profiles)) return payload.profiles; + return []; +}; + +const buildTokenIndex = (tokenProfiles) => { + const byTenantKey = new Map(); + const byEmail = new Map(); + + for (const profile of tokenProfiles) { + if (profile?.tenantKey) byTenantKey.set(profile.tenantKey, profile); + if (profile?.email) byEmail.set(profile.email, profile); + } + + return { + byTenantKey, + byEmail, + find(manifestRow) { + if (manifestRow.tenantKey && byTenantKey.has(manifestRow.tenantKey)) { + return byTenantKey.get(manifestRow.tenantKey); + } + if (manifestRow.email && byEmail.has(manifestRow.email)) { + return byEmail.get(manifestRow.email); + } + return null; + }, + }; +}; + +const unique = (items) => [...new Set(items.filter(Boolean))]; + +const buildGraphqlNames = (tableName) => { + const typeName = toPascalCase(tableName); + const nodeField = toCamelCase(typeName); + const queryField = `${nodeField}s`; + + return { + typeName, + nodeField, + queryField, + createMutation: `create${typeName}`, + updateMutation: `update${typeName}`, + deleteMutation: `delete${typeName}`, + createInputType: `Create${typeName}Input`, + updateInputType: `Update${typeName}Input`, + patchField: `${nodeField}Patch`, + }; +}; + +const getRequestedModes = (mode) => { + if (mode === 'dual') return ['private', 'public']; + return [mode]; +}; + +const toHost = (domain, subdomain) => (subdomain ? `${subdomain}.${domain}` : domain); + +const scorePublicRoute = ({ apiName, subdomain }, { publicApiName: targetApiName, publicSubdomainPrefix: prefix }) => { + let score = 0; + if (apiName === targetApiName) score += 20; + if (typeof subdomain === 'string' && subdomain.length > 0) { + score += 5; + if (prefix && subdomain.startsWith(prefix)) score += 10; + } + return score; +}; + +const pickBestPublicRoute = (routes, scoringOptions) => { + if (!Array.isArray(routes) || routes.length === 0) return null; + const ranked = [...routes] + .map((row) => ({ row, score: scorePublicRoute(row, scoringOptions) })) + .sort((a, b) => b.score - a.score || String(a.row.host).localeCompare(String(b.row.host))); + return ranked[0]?.row ?? null; +}; + +const resolvePublicHostMap = async ({ databaseIds, pgConfig, publicApiName, publicSubdomainPrefix }) => { + const hostByDatabaseId = new Map(); + if (!Array.isArray(databaseIds) || databaseIds.length === 0) { + return hostByDatabaseId; + } + + const pool = new Pool(pgConfig); + try { + const sql = ` + select + a.database_id::text as database_id, + a.name as api_name, + d.domain, + d.subdomain + from services_public.apis a + join services_public.domains d on d.api_id = a.id + where a.is_public = true + and a.database_id::text = any($1::text[]) + `; + + const result = await pool.query(sql, [databaseIds]); + const grouped = new Map(); + + for (const row of result.rows ?? []) { + const databaseId = row.database_id; + const domain = row.domain; + const subdomain = row.subdomain; + const apiName = row.api_name; + if (!databaseId || !domain) continue; + + const host = toHost(domain, subdomain); + if (!grouped.has(databaseId)) { + grouped.set(databaseId, []); + } + + grouped.get(databaseId).push({ + databaseId, + apiName, + domain, + subdomain, + host, + }); + } + + for (const databaseId of databaseIds) { + const candidates = grouped.get(databaseId) ?? []; + const best = pickBestPublicRoute(candidates, { publicApiName, publicSubdomainPrefix }); + if (best?.host) { + hostByDatabaseId.set(databaseId, best.host); + } + } + + return hostByDatabaseId; + } finally { + await pool.end(); + } +}; + +const buildHeadersForMode = ({ mode, tokenProfile, row, publicHost }) => { + const headers = {}; + + if (mode === 'private') { + headers.Host = tokenProfile.headers?.Host || 'localhost'; + headers['X-Database-Id'] = row.databaseId; + headers['X-Schemata'] = row.physicalSchema; + } else { + headers.Host = publicHost; + } + + if (tokenProfile.headers?.Authorization) { + headers.Authorization = tokenProfile.headers.Authorization; + } + + return headers; +}; + +const buildBaseProfile = ({ mode, row, tokenProfile }) => { + const gqlNames = buildGraphqlNames(row.tableName); + + const availableSchemas = unique( + (Array.isArray(row.availableSchemas) ? row.availableSchemas : []) + .map((item) => (typeof item === 'string' ? item : item?.schemaName)) + .concat([row.physicalSchema]), + ); + + return { + key: `business:${row.tenantKey ?? row.email ?? row.tableName}`, + mode: mode === 'private' ? 'business-table-private' : 'business-table-public', + routingMode: mode, + tenantKey: row.tenantKey ?? null, + email: row.email ?? null, + graphqlUrl: tokenProfile.graphqlUrl || '/graphql', + table: { + databaseId: row.databaseId, + schemaId: row.schemaId ?? null, + tableId: row.tableId ?? null, + tableName: row.tableName, + physicalSchema: row.physicalSchema, + availableSchemas, + ...gqlNames, + }, + seed: { + rowId: row.seedRowId ?? null, + }, + operationWeights: { + create: 0.2, + getById: 0.4, + updateById: 0.2, + listRecent: 0.2, + }, + }; +}; + +const buildOutputPayload = ({ + routingMode, + runDir, + baseUrl, + manifestPath, + tokensPath, + totalManifestRows, + totalTokenProfiles, + profiles, + failures, + meta, +}) => ({ + createdAt: new Date().toISOString(), + runDir, + baseUrl, + routingMode, + manifestPath, + tokensPath, + totalManifestRows, + totalTokenProfiles, + successCount: profiles.length, + failureCount: failures.length, + failures, + meta, + profiles, +}); + +const main = async () => { + if (!VALID_ROUTING_MODES.has(routingMode)) { + throw new Error(`Invalid --routing-mode=${routingMode}; expected private|public|dual`); + } + + if (!VALID_ROUTING_MODES.has(compatRoutingMode) || compatRoutingMode === 'dual') { + throw new Error(`Invalid --compat-routing-mode=${compatRoutingMode}; expected private|public`); + } + + await ensureRunDirs(runDir); + + const manifest = await loadJson(manifestPath); + if (!Array.isArray(manifest) || manifest.length === 0) { + throw new Error(`No manifest rows found in ${manifestPath}`); + } + + const tokenPayload = await loadJson(tokensPath); + const tokenProfiles = extractTokenProfiles(tokenPayload); + if (tokenProfiles.length === 0) { + throw new Error(`No token profiles found in ${tokensPath}`); + } + + const tokenIndex = buildTokenIndex(tokenProfiles); + const requestedModes = getRequestedModes(routingMode); + const profileSets = { + private: [], + public: [], + }; + const failureSets = { + private: [], + public: [], + }; + + const manifestDatabaseIds = unique(manifest.map((row) => row?.databaseId).filter(Boolean)); + const publicHostByDatabaseId = requestedModes.includes('public') + ? await resolvePublicHostMap({ + databaseIds: manifestDatabaseIds, + pgConfig, + publicApiName, + publicSubdomainPrefix, + }) + : new Map(); + + for (const row of manifest) { + const tokenProfile = tokenIndex.find(row); + const baseFailure = { + tenantKey: row?.tenantKey ?? null, + email: row?.email ?? null, + }; + + if (!tokenProfile) { + for (const mode of requestedModes) { + failureSets[mode].push({ ...baseFailure, reason: 'matching token profile not found' }); + } + continue; + } + + if (!row.databaseId || !row.physicalSchema || !row.tableName) { + for (const mode of requestedModes) { + failureSets[mode].push({ + ...baseFailure, + reason: 'manifest row missing databaseId/physicalSchema/tableName', + }); + } + continue; + } + + for (const mode of requestedModes) { + const baseProfile = buildBaseProfile({ mode, row, tokenProfile }); + + if (mode === 'public') { + const publicHost = publicHostByDatabaseId.get(row.databaseId); + if (!publicHost) { + failureSets.public.push({ + ...baseFailure, + databaseId: row.databaseId, + reason: `public host not found (api=${publicApiName})`, + }); + continue; + } + + baseProfile.headers = buildHeadersForMode({ + mode, + tokenProfile, + row, + publicHost, + }); + profileSets.public.push(baseProfile); + } else { + baseProfile.headers = buildHeadersForMode({ + mode, + tokenProfile, + row, + publicHost: null, + }); + profileSets.private.push(baseProfile); + } + } + } + + const privatePayload = buildOutputPayload({ + routingMode: 'private', + runDir, + baseUrl, + manifestPath, + tokensPath, + totalManifestRows: manifest.length, + totalTokenProfiles: tokenProfiles.length, + profiles: profileSets.private, + failures: failureSets.private, + meta: { + publicApiName, + publicSubdomainPrefix, + }, + }); + + const publicPayload = buildOutputPayload({ + routingMode: 'public', + runDir, + baseUrl, + manifestPath, + tokensPath, + totalManifestRows: manifest.length, + totalTokenProfiles: tokenProfiles.length, + profiles: profileSets.public, + failures: failureSets.public, + meta: { + publicApiName, + publicSubdomainPrefix, + resolvedPublicHosts: publicHostByDatabaseId.size, + }, + }); + + const writtenPaths = []; + + if (requestedModes.includes('private')) { + await writeJson(privateOutputPath, privatePayload); + writtenPaths.push({ mode: 'private', path: privateOutputPath, successCount: privatePayload.successCount }); + } + + if (requestedModes.includes('public')) { + await writeJson(publicOutputPath, publicPayload); + writtenPaths.push({ mode: 'public', path: publicOutputPath, successCount: publicPayload.successCount }); + } + + const compatPayload = compatRoutingMode === 'public' ? publicPayload : privatePayload; + const compatSupported = requestedModes.includes(compatRoutingMode); + + if (!compatSupported) { + throw new Error( + `compat routing mode (${compatRoutingMode}) not generated in --routing-mode=${routingMode}; ` + + 'set --compat-routing-mode to one of the generated modes', + ); + } + + await writeJson(outputPath, compatPayload); + + console.log( + JSON.stringify( + { + outputPath, + routingMode, + compatRoutingMode, + privateOutputPath: requestedModes.includes('private') ? privateOutputPath : null, + publicOutputPath: requestedModes.includes('public') ? publicOutputPath : null, + writtenPaths, + counts: { + privateSuccess: privatePayload.successCount, + privateFailure: privatePayload.failureCount, + publicSuccess: publicPayload.successCount, + publicFailure: publicPayload.failureCount, + }, + }, + null, + 2, + ), + ); + + if (compatPayload.successCount === 0) { + process.exitCode = 2; + } +}; + +main().catch((error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exit(1); +}); diff --git a/graphql/server/perf/src/legacy/build-keyspace-profiles.ts b/graphql/server/perf/src/legacy/build-keyspace-profiles.ts new file mode 100644 index 0000000000..e81a10b06b --- /dev/null +++ b/graphql/server/perf/src/legacy/build-keyspace-profiles.ts @@ -0,0 +1,334 @@ +#!/usr/bin/env node +// @ts-nocheck + +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import { + DEFAULT_BASE_URL, + DEFAULT_TMP_ROOT, + ensureRunDirs, + getArgValue, + makeRunId, + postJson, + writeJson, +} from './common'; + +const args = process.argv.slice(2); + +const runId = getArgValue(args, '--run-id', makeRunId()); +const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId))); +const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL); +const inputPath = path.resolve(getArgValue(args, '--input', path.join(runDir, 'data', 'tokens.json'))); +const outputPath = path.resolve( + getArgValue(args, '--output', path.join(runDir, 'data', 'tokens.keyspace.json')), +); +const readyProfilesPath = path.resolve( + getArgValue(args, '--ready-profiles', path.join(runDir, 'data', 'request-profiles.ready.json')), +); + +const mode = getArgValue(args, '--mode', 'schemata'); +const targetRouteKeys = Number.parseInt(getArgValue(args, '--target-route-keys', '20'), 10); +const maxSchemaWidth = Math.max(1, Number.parseInt(getArgValue(args, '--max-schema-width', '3'), 10)); +const maxProfiles = Math.max(1, Number.parseInt(getArgValue(args, '--max-profiles', '5000'), 10)); +const noSmokeCheck = args.includes('--no-smoke-check'); +const smokeQuery = getArgValue(args, '--smoke-query', '{ __typename }'); + +const schemaPool = getArgValue( + args, + '--schema-pool', + 'constructive_public,constructive_auth_public,constructive_users_public,constructive_memberships_public,services_public', +) + .split(',') + .map((s) => s.trim()) + .filter(Boolean); + +const uniqueBy = (items, keyFn) => { + const seen = new Set(); + const out = []; + for (const item of items) { + const key = keyFn(item); + if (!seen.has(key)) { + seen.add(key); + out.push(item); + } + } + return out; +}; + +const routeKeyFromHeaders = (headers = {}) => { + const apiName = headers['X-Api-Name']; + const databaseId = headers['X-Database-Id']; + const schemata = headers['X-Schemata']; + const metaSchema = headers['X-Meta-Schema']; + const host = headers.Host || 'localhost'; + + if (apiName && databaseId) return `api:${databaseId}:${apiName}`; + if (schemata && databaseId) return `schemata:${databaseId}:${schemata}`; + if (metaSchema && databaseId) return `metaschema:api:${databaseId}`; + return host; +}; + +const normalizeRoutingHeaders = (headers = {}) => { + const out = {}; + if (headers.Host) out.Host = headers.Host; + if (headers['X-Api-Name']) out['X-Api-Name'] = headers['X-Api-Name']; + if (headers['X-Database-Id']) out['X-Database-Id'] = headers['X-Database-Id']; + if (headers['X-Schemata']) out['X-Schemata'] = headers['X-Schemata']; + if (headers['X-Meta-Schema']) out['X-Meta-Schema'] = headers['X-Meta-Schema']; + return out; +}; + +const generateSchemaCombinations = (schemas, maxWidth, hardLimit) => { + const out = []; + + const walk = (startIndex, prefix) => { + if (out.length >= hardLimit) return; + if (prefix.length > 0) { + out.push([...prefix]); + } + if (prefix.length === maxWidth) return; + for (let i = startIndex; i < schemas.length; i += 1) { + prefix.push(schemas[i]); + walk(i + 1, prefix); + prefix.pop(); + if (out.length >= hardLimit) return; + } + }; + + walk(0, []); + return out; +}; + +const extractTokenProfiles = async (filePath) => { + const raw = await fs.readFile(filePath, 'utf8'); + const parsed = JSON.parse(raw); + const profiles = Array.isArray(parsed) ? parsed : parsed?.profiles; + if (!Array.isArray(profiles) || profiles.length === 0) { + throw new Error(`No profiles found in ${filePath}`); + } + return { raw: parsed, profiles }; +}; + +const readReadyProfiles = async (filePath) => { + try { + const raw = await fs.readFile(filePath, 'utf8'); + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +}; + +const buildCandidateRoutes = async ({ baseProfiles }) => { + const candidates = []; + + for (const profile of baseProfiles) { + const headers = normalizeRoutingHeaders(profile.headers); + const routeKey = routeKeyFromHeaders(headers); + candidates.push({ + type: 'existing', + routeKey, + headers, + }); + } + + const wantApi = mode === 'api' || mode === 'mixed'; + const wantSchemata = mode === 'schemata' || mode === 'mixed'; + + if (wantApi) { + const readyProfiles = await readReadyProfiles(readyProfilesPath); + for (const profile of readyProfiles) { + if (profile?.mode !== 'private-header-routing') continue; + const headers = normalizeRoutingHeaders(profile.headers); + if (!headers['X-Api-Name'] || !headers['X-Database-Id']) continue; + candidates.push({ + type: 'api', + routeKey: routeKeyFromHeaders(headers), + headers, + }); + } + } + + if (wantSchemata) { + const dbToHost = new Map(); + for (const profile of baseProfiles) { + const db = profile.headers?.['X-Database-Id']; + if (!db) continue; + if (!dbToHost.has(db)) { + dbToHost.set(db, profile.headers?.Host || 'localhost'); + } + } + + const combos = generateSchemaCombinations(schemaPool, maxSchemaWidth, targetRouteKeys * 4); + for (const [databaseId, host] of dbToHost.entries()) { + for (const combo of combos) { + const headers = { + Host: host, + 'X-Database-Id': databaseId, + 'X-Schemata': combo.join(','), + }; + candidates.push({ + type: 'schemata', + routeKey: routeKeyFromHeaders(headers), + headers, + }); + } + } + } + + return uniqueBy(candidates, (route) => route.routeKey); +}; + +const smokeRoute = async ({ route, probeProfile }) => { + const headers = { + Authorization: probeProfile.headers?.Authorization, + ...route.headers, + }; + const result = await postJson({ + url: `${baseUrl}${probeProfile.graphqlUrl ?? '/graphql'}`, + headers, + payload: { query: smokeQuery }, + timeoutMs: 15000, + }); + + const hasErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0; + const ok = + result.ok && + !hasErrors && + (result.json?.data?.__typename === 'Query' || result.json?.data != null); + + return { + ok, + status: result.status, + elapsedMs: result.elapsedMs, + error: result.error ?? result.json?.errors?.[0]?.message ?? null, + }; +}; + +const pickRouteSet = async ({ baseProfiles, candidates }) => { + const selected = []; + const diagnostics = []; + + const probeByDb = new Map(); + for (const profile of baseProfiles) { + const db = profile.headers?.['X-Database-Id'] || '__host__'; + if (!probeByDb.has(db)) { + probeByDb.set(db, profile); + } + } + + for (const route of candidates) { + if (selected.length >= targetRouteKeys) break; + + if (noSmokeCheck) { + selected.push(route); + continue; + } + + const db = route.headers?.['X-Database-Id'] || '__host__'; + const probeProfile = probeByDb.get(db) ?? baseProfiles[0]; + const smoke = await smokeRoute({ route, probeProfile }); + diagnostics.push({ routeKey: route.routeKey, type: route.type, ...smoke }); + if (smoke.ok) { + selected.push(route); + } + } + + return { selected, diagnostics }; +}; + +const mergeRouteHeaders = (baseHeaders = {}, routeHeaders = {}) => { + const out = { ...baseHeaders }; + delete out['X-Api-Name']; + delete out['X-Database-Id']; + delete out['X-Schemata']; + delete out['X-Meta-Schema']; + + return { + ...out, + ...routeHeaders, + }; +}; + +const expandProfiles = ({ baseProfiles, routes }) => { + const out = []; + for (const profile of baseProfiles) { + const baseDb = profile.headers?.['X-Database-Id'] ?? null; + for (const route of routes) { + const routeDb = route.headers?.['X-Database-Id'] ?? null; + if (routeDb && baseDb && routeDb !== baseDb) { + continue; + } + out.push({ + ...profile, + key: `${profile.key}|${route.routeKey}`, + routeKey: route.routeKey, + routeType: route.type, + headers: mergeRouteHeaders(profile.headers, route.headers), + }); + if (out.length >= maxProfiles) { + return out; + } + } + } + return out; +}; + +const main = async () => { + await ensureRunDirs(runDir); + const { raw: inputPayload, profiles: baseProfiles } = await extractTokenProfiles(inputPath); + + const candidates = await buildCandidateRoutes({ baseProfiles }); + const { selected, diagnostics } = await pickRouteSet({ baseProfiles, candidates }); + + if (selected.length === 0) { + throw new Error( + 'No valid routes selected for keyspace expansion. Try --no-smoke-check or adjust --schema-pool.', + ); + } + + const expandedProfiles = expandProfiles({ baseProfiles, routes: selected }); + if (expandedProfiles.length === 0) { + throw new Error('Expanded profile set is empty.'); + } + + const payload = { + createdAt: new Date().toISOString(), + baseUrl, + inputPath, + mode, + targetRouteKeys, + selectedRouteKeys: selected.length, + schemaPool, + noSmokeCheck, + smokeQuery, + totalInputProfiles: baseProfiles.length, + totalOutputProfiles: expandedProfiles.length, + selectedRoutes: selected, + diagnostics, + profiles: expandedProfiles, + source: inputPayload, + }; + + await writeJson(outputPath, payload); + + console.log( + JSON.stringify( + { + outputPath, + mode, + selectedRouteKeys: selected.length, + totalInputProfiles: baseProfiles.length, + totalOutputProfiles: expandedProfiles.length, + }, + null, + 2, + ), + ); +}; + +main().catch((error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exit(1); +}); diff --git a/graphql/server/perf/src/legacy/build-token-pool.ts b/graphql/server/perf/src/legacy/build-token-pool.ts new file mode 100644 index 0000000000..90eca1133c --- /dev/null +++ b/graphql/server/perf/src/legacy/build-token-pool.ts @@ -0,0 +1,162 @@ +#!/usr/bin/env node +// @ts-nocheck + +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import { + DEFAULT_BASE_URL, + DEFAULT_TMP_ROOT, + ensureRunDirs, + getArgValue, + makeRunId, + postJson, + writeJson, +} from './common'; + +const args = process.argv.slice(2); + +const runId = getArgValue(args, '--run-id', makeRunId()); +const runDir = path.resolve( + getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)), +); +const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL); +const credentialsPath = path.resolve( + getArgValue(args, '--credentials', path.join(runDir, 'data', 'tenant-credentials.json')), +); +const outputPath = path.resolve( + getArgValue(args, '--output', path.join(runDir, 'data', 'tokens.json')), +); + +const signInMutation = ` +mutation SignIn($input: SignInInput!) { + signIn(input: $input) { + result { + id + userId + accessToken + accessTokenExpiresAt + } + } +} +`; + +const toRouteHeaders = (row) => { + if (row.apiName && row.databaseId) { + return { + Host: row.host || 'localhost', + 'X-Api-Name': row.apiName, + 'X-Database-Id': row.databaseId, + }; + } + + if (row.host) { + return { Host: row.host }; + } + + throw new Error( + `credential row requires either {apiName,databaseId} or {host}; row=${JSON.stringify({ + tenantKey: row.tenantKey ?? null, + email: row.email ?? null, + })}`, + ); +}; + +const main = async () => { + await ensureRunDirs(runDir); + + const raw = await fs.readFile(credentialsPath, 'utf8'); + const credentials = JSON.parse(raw); + + if (!Array.isArray(credentials) || credentials.length === 0) { + throw new Error(`No credential rows found in ${credentialsPath}`); + } + + const results = []; + const failures = []; + + for (const row of credentials) { + const headers = toRouteHeaders(row); + const response = await postJson({ + url: `${baseUrl}/graphql`, + headers, + payload: { + query: signInMutation, + variables: { + input: { + email: row.email, + password: row.password, + rememberMe: true, + }, + }, + }, + timeoutMs: 20000, + }); + + const signInResult = response.json?.data?.signIn?.result; + const accessToken = signInResult?.accessToken; + + if (!response.ok || !accessToken) { + failures.push({ + tenantKey: row.tenantKey ?? null, + email: row.email ?? null, + status: response.status, + error: + response.error ?? + response.json?.errors?.[0]?.message ?? + 'signIn did not return accessToken', + }); + continue; + } + + results.push({ + key: `token:${row.tenantKey ?? row.email}`, + mode: 'auth-token', + tenantKey: row.tenantKey ?? null, + email: row.email, + userId: signInResult.userId ?? null, + tokenId: signInResult.id ?? null, + accessTokenExpiresAt: signInResult.accessTokenExpiresAt ?? null, + graphqlUrl: '/graphql', + headers: { + ...headers, + Authorization: `Bearer ${accessToken}`, + }, + }); + } + + const payload = { + createdAt: new Date().toISOString(), + baseUrl, + credentialsPath, + totalInput: credentials.length, + successCount: results.length, + failureCount: failures.length, + failures, + profiles: results, + }; + + await writeJson(outputPath, payload); + + console.log( + JSON.stringify( + { + outputPath, + totalInput: credentials.length, + successCount: results.length, + failureCount: failures.length, + }, + null, + 2, + ), + ); + + if (failures.length > 0 && results.length === 0) { + process.exit(2); + } +}; + +main().catch((error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exit(1); +}); diff --git a/graphql/server/perf/src/legacy/common.ts b/graphql/server/perf/src/legacy/common.ts new file mode 100644 index 0000000000..549fe513d4 --- /dev/null +++ b/graphql/server/perf/src/legacy/common.ts @@ -0,0 +1,232 @@ +// @ts-nocheck +import fs from 'node:fs/promises'; +import http from 'node:http'; +import https from 'node:https'; +import path from 'node:path'; + +export const DEFAULT_TMP_ROOT = '/tmp/constructive-perf'; +export const DEFAULT_BASE_URL = 'http://localhost:3000'; + +export const getArgValue = (args, flag, fallback = null) => { + const index = args.indexOf(flag); + if (index === -1 || index === args.length - 1) { + return fallback; + } + return args[index + 1]; +}; + +export const hasFlag = (args, flag) => args.includes(flag); + +export const parseIntArg = (value, fallback) => { + const parsed = Number.parseInt(String(value ?? ''), 10); + return Number.isFinite(parsed) ? parsed : fallback; +}; + +export const toIsoFileTime = (date = new Date()) => + date.toISOString().replace(/[:.]/g, '-'); + +export const makeRunId = (prefix = 'graphile-cache-leak') => + `${prefix}-${toIsoFileTime(new Date())}-pid${process.pid}`; + +export const ensureRunDirs = async (runDir) => { + const dirs = { + runDir, + logsDir: path.join(runDir, 'logs'), + samplerDir: path.join(runDir, 'logs', 'sampler'), + heapDir: path.join(runDir, 'logs', 'heap'), + dataDir: path.join(runDir, 'data'), + reportsDir: path.join(runDir, 'reports'), + tmpScriptsDir: path.join(runDir, 'tmp-scripts'), + }; + + await Promise.all(Object.values(dirs).map((dir) => fs.mkdir(dir, { recursive: true }))); + return dirs; +}; + +export const writeJson = async (filePath, payload) => { + await fs.mkdir(path.dirname(filePath), { recursive: true }); + await fs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8'); +}; + +export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +const hasHostHeader = (headers = {}) => + Object.keys(headers || {}).some((key) => key.toLowerCase() === 'host'); + +const requestJsonViaNodeHttp = async ({ url, method, headers = {}, body = null, timeoutMs = 15000 }) => { + const startedAt = Date.now(); + + return await new Promise((resolve) => { + const target = new URL(url); + const client = target.protocol === 'https:' ? https : http; + const requestHeaders = { ...headers }; + if (body != null && requestHeaders['Content-Length'] == null && requestHeaders['content-length'] == null) { + requestHeaders['Content-Length'] = Buffer.byteLength(body); + } + + const req = client.request( + { + protocol: target.protocol, + hostname: target.hostname, + port: target.port || (target.protocol === 'https:' ? 443 : 80), + path: `${target.pathname}${target.search}`, + method, + headers: requestHeaders, + }, + (res) => { + const chunks = []; + res.on('data', (chunk) => chunks.push(chunk)); + res.on('end', () => { + const text = Buffer.concat(chunks).toString('utf8'); + let json = null; + try { + json = JSON.parse(text); + } catch { + json = null; + } + resolve({ + ok: Number(res.statusCode ?? 0) >= 200 && Number(res.statusCode ?? 0) < 300, + status: Number(res.statusCode ?? 0), + elapsedMs: Date.now() - startedAt, + json, + text, + }); + }); + }, + ); + + req.on('error', (error) => { + resolve({ + ok: false, + status: 0, + elapsedMs: Date.now() - startedAt, + error: error instanceof Error ? error.message : String(error), + }); + }); + + req.setTimeout(timeoutMs, () => { + req.destroy(new Error(`Request timeout after ${timeoutMs}ms`)); + }); + + if (body != null) { + req.write(body); + } + req.end(); + }); +}; + +export const postJson = async ({ url, payload, headers = {}, timeoutMs = 15000 }) => { + const mergedHeaders = { + 'Content-Type': 'application/json', + ...headers, + }; + + if (hasHostHeader(mergedHeaders)) { + return await requestJsonViaNodeHttp({ + url, + method: 'POST', + headers: mergedHeaders, + body: JSON.stringify(payload), + timeoutMs, + }); + } + + const startedAt = Date.now(); + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), timeoutMs); + + try { + const response = await fetch(url, { + method: 'POST', + headers: mergedHeaders, + body: JSON.stringify(payload), + signal: controller.signal, + }); + + const text = await response.text(); + let json = null; + try { + json = JSON.parse(text); + } catch { + json = null; + } + + return { + ok: response.ok, + status: response.status, + elapsedMs: Date.now() - startedAt, + json, + text, + }; + } catch (error) { + return { + ok: false, + status: 0, + elapsedMs: Date.now() - startedAt, + error: error instanceof Error ? error.message : String(error), + }; + } finally { + clearTimeout(timeout); + } +}; + +export const getJson = async ({ url, headers = {}, timeoutMs = 10000 }) => { + if (hasHostHeader(headers)) { + return await requestJsonViaNodeHttp({ + url, + method: 'GET', + headers, + timeoutMs, + }); + } + + const startedAt = Date.now(); + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), timeoutMs); + + try { + const response = await fetch(url, { + method: 'GET', + headers, + signal: controller.signal, + }); + const text = await response.text(); + let json = null; + try { + json = JSON.parse(text); + } catch { + json = null; + } + return { + ok: response.ok, + status: response.status, + elapsedMs: Date.now() - startedAt, + json, + text, + }; + } catch (error) { + return { + ok: false, + status: 0, + elapsedMs: Date.now() - startedAt, + error: error instanceof Error ? error.message : String(error), + }; + } finally { + clearTimeout(timeout); + } +}; + +export const stableStringify = (value) => JSON.stringify(value, Object.keys(value).sort()); + +export const dedupeBy = (items, makeKey) => { + const seen = new Set(); + const out = []; + for (const item of items) { + const key = makeKey(item); + if (!seen.has(key)) { + seen.add(key); + out.push(item); + } + } + return out; +}; diff --git a/graphql/server/perf/src/legacy/phase1-preflight.ts b/graphql/server/perf/src/legacy/phase1-preflight.ts new file mode 100644 index 0000000000..a49ae0d372 --- /dev/null +++ b/graphql/server/perf/src/legacy/phase1-preflight.ts @@ -0,0 +1,808 @@ +#!/usr/bin/env node +// @ts-nocheck + +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { spawn } from 'node:child_process'; +import { Pool } from 'pg'; + +import { + DEFAULT_BASE_URL, + DEFAULT_TMP_ROOT, + dedupeBy, + ensureRunDirs, + getArgValue, + getJson, + hasFlag, + makeRunId, + postJson, + stableStringify, + writeJson, +} from './common'; + +const args = process.argv.slice(2); + +const runId = getArgValue(args, '--run-id', makeRunId()); +const tmpRoot = path.resolve(getArgValue(args, '--tmp-root', DEFAULT_TMP_ROOT)); +const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(tmpRoot, runId))); +const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL); + +const requireTenants = Number.parseInt(getArgValue(args, '--min-tenant-count', '10'), 10); +const enforceTenantScale = !hasFlag(args, '--allow-underprovisioned'); + +const defaultMinTokenTenants = enforceTenantScale ? String(requireTenants) : '1'; +const minTokenTenants = Number.parseInt( + getArgValue(args, '--min-token-tenants', defaultMinTokenTenants), + 10, +); +const recommendedTokenTenants = Number.parseInt( + getArgValue(args, '--recommended-token-tenants', String(requireTenants)), + 10, +); +const bootstrapDefaultCredential = !hasFlag(args, '--no-bootstrap-default-credential'); + +const defaultSigninEmail = getArgValue( + args, + '--default-signin-email', + process.env.PERF_SIGNIN_EMAIL || 'admin@constructive.io', +); +const defaultSigninPassword = getArgValue( + args, + '--default-signin-password', + process.env.PERF_SIGNIN_PASSWORD || 'admin123!@Constructive', +); + +const credentialsPath = path.resolve( + getArgValue(args, '--credentials', path.join(runDir, 'data', 'tenant-credentials.json')), +); +const tokenOutputPath = path.resolve( + getArgValue(args, '--token-output', path.join(runDir, 'data', 'tokens.json')), +); +const dbTechValidationPath = path.resolve( + getArgValue(args, '--db-tech-validation', path.join(runDir, 'reports', 'db-tech-validation.json')), +); +const businessManifestPath = path.resolve( + getArgValue(args, '--business-manifest', path.join(runDir, 'data', 'business-table-manifest.json')), +); +const businessProfilesOutputPath = path.resolve( + getArgValue( + args, + '--business-profiles-output', + path.join(runDir, 'data', 'business-op-profiles.json'), + ), +); +const runDbpmTechValidation = !hasFlag(args, '--skip-dbpm-tech-validation'); +const dbpmTenantCount = Number.parseInt( + getArgValue(args, '--dbpm-tenant-count', String(Math.max(requireTenants, 2))), + 10, +); +const dbpmUserPassword = getArgValue(args, '--dbpm-user-password', 'Constructive!23456'); +const dbpmUserPrefix = getArgValue(args, '--dbpm-user-prefix', `dbpm-preflight-${Date.now()}`); +const dbpmShapeVariants = Number.parseInt( + getArgValue(args, '--dbpm-shape-variants', '0'), + 10, +); +const dbpmRouteHost = getArgValue(args, '--route-host', 'localhost'); +const dbpmPrivateApiName = getArgValue(args, '--private-api-name', 'private'); +const dbpmPrivateDatabaseId = getArgValue( + args, + '--private-database-id', + '028752cb-510b-1438-2f39-64534bd1cbd7', +); +const dbpmAuthHost = getArgValue(args, '--auth-host', ''); +const dbpmProvisionHost = getArgValue(args, '--provision-host', ''); +const businessRoutingMode = getArgValue(args, '--business-routing-mode', 'public'); +const businessCompatRoutingMode = getArgValue(args, '--business-compat-routing-mode', businessRoutingMode); +const businessPublicApiName = getArgValue(args, '--business-public-api-name', 'api'); +const businessPublicSubdomainPrefix = getArgValue( + args, + '--business-public-subdomain-prefix', + 'api-dbpm-', +); +const keyspaceOutputPath = path.resolve( + getArgValue(args, '--keyspace-output', path.join(runDir, 'data', 'tokens.keyspace.json')), +); +const keyspaceMode = getArgValue(args, '--keyspace-mode', 'schemata'); +const keyspaceTargetRouteKeys = Number.parseInt( + getArgValue(args, '--keyspace-target-route-keys', '24'), + 10, +); +const keyspaceMinRouteKeys = Number.parseInt( + getArgValue(args, '--keyspace-min-route-keys', '16'), + 10, +); +const keyspaceMaxProfiles = Number.parseInt(getArgValue(args, '--keyspace-max-profiles', '5000'), 10); +const keyspaceMaxSchemaWidth = Number.parseInt( + getArgValue(args, '--keyspace-max-schema-width', '3'), + 10, +); +const keyspaceSchemaPool = getArgValue( + args, + '--keyspace-schema-pool', + 'constructive_public,constructive_auth_public,constructive_users_public,constructive_memberships_public,services_public', +); +const keyspaceNoSmokeCheck = hasFlag(args, '--keyspace-no-smoke-check'); + +const pgConfig = { + host: getArgValue(args, '--pg-host', process.env.PGHOST || 'localhost'), + port: Number.parseInt(getArgValue(args, '--pg-port', process.env.PGPORT || '5432'), 10), + database: getArgValue(args, '--pg-database', process.env.PGDATABASE || 'constructive'), + user: getArgValue(args, '--pg-user', process.env.PGUSER || 'postgres'), + password: getArgValue(args, '--pg-password', process.env.PGPASSWORD || 'password'), +}; + +const queries = { + tenantBaseline: ` + select + (select count(*)::int from constructive_users_public.users where type = 2) as org_count, + (select count(distinct entity_id)::int from constructive_memberships_public.org_memberships) as org_membership_org_count, + (select count(distinct actor_id)::int from constructive_memberships_public.org_memberships) as org_membership_actor_count, + (select count(distinct database_id)::int from services_public.apis) as api_distinct_database_count, + (select count(*)::int from services_public.apis where is_public = true) as public_api_count, + (select count(*)::int from services_public.apis where is_public = false) as private_api_count + `, + apiCandidates: ` + select + a.id::text as api_id, + a.name as api_name, + a.database_id::text as database_id, + a.is_public, + d.domain, + d.subdomain + from services_public.apis a + left join services_public.domains d on d.api_id = a.id + order by a.is_public, a.name, a.database_id + `, +}; + +const pathExists = async (targetPath) => { + try { + await fs.access(targetPath); + return true; + } catch { + return false; + } +}; + +const buildProfiles = (rows) => { + const privateProfiles = rows + .filter((row) => row.is_public === false) + .map((row) => ({ + key: `private:${row.database_id}:${row.api_name}`, + mode: 'private-header-routing', + databaseId: row.database_id, + apiName: row.api_name, + graphqlUrl: '/graphql', + headers: { + Host: 'localhost', + 'X-Api-Name': row.api_name, + 'X-Database-Id': row.database_id, + }, + })); + + const publicProfiles = rows + .filter((row) => row.is_public === true && row.domain) + .map((row) => { + const host = row.subdomain ? `${row.subdomain}.${row.domain}` : row.domain; + return { + key: `public:${host}`, + mode: 'public-domain-routing', + graphqlUrl: '/graphql', + headers: { + Host: host, + }, + }; + }); + + return dedupeBy([...privateProfiles, ...publicProfiles], (profile) => stableStringify(profile)); +}; + +const smokeCheckGraphql = async ({ baseUrl: root, profiles }) => { + const checks = []; + for (const profile of profiles) { + const result = await postJson({ + url: `${root}${profile.graphqlUrl}`, + headers: profile.headers, + payload: { query: '{ __typename }' }, + timeoutMs: 15000, + }); + + const hasTypename = result.json?.data?.__typename === 'Query'; + const hasErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0; + + checks.push({ + profileKey: profile.key, + ok: result.ok && hasTypename, + status: result.status, + elapsedMs: result.elapsedMs, + hasTypename, + hasErrors, + error: result.error || null, + firstError: hasErrors ? result.json.errors[0]?.message ?? 'unknown GraphQL error' : null, + }); + } + return checks; +}; + +const buildBootstrapCredentials = ({ readyProfiles }) => { + const privateProfiles = readyProfiles.filter((profile) => profile.mode === 'private-header-routing'); + + return privateProfiles.map((profile, index) => ({ + tenantKey: profile.key || `tenant-${index + 1}`, + email: defaultSigninEmail, + password: defaultSigninPassword, + host: profile.headers?.Host || 'localhost', + apiName: profile.headers?.['X-Api-Name'], + databaseId: profile.headers?.['X-Database-Id'], + })); +}; + +const runTokenBuilder = async () => { + const scriptPath = path.join(__dirname, 'build-token-pool.ts'); + + return await new Promise((resolve) => { + const child = spawn( + 'npx', + [ + 'ts-node', + scriptPath, + '--run-dir', + runDir, + '--base-url', + baseUrl, + '--credentials', + credentialsPath, + '--output', + tokenOutputPath, + ], + { stdio: ['ignore', 'pipe', 'pipe'] }, + ); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (chunk) => { + stdout += String(chunk); + }); + child.stderr.on('data', (chunk) => { + stderr += String(chunk); + }); + + child.on('close', (code) => { + resolve({ + code, + ok: code === 0, + stdout: stdout.trim(), + stderr: stderr.trim(), + }); + }); + }); +}; + +const runDbpmTechValidationScript = async () => { + const scriptPath = path.join(__dirname, 'phase1-tech-validate-dbpm.ts'); + + return await new Promise((resolve) => { + const child = spawn( + 'npx', + [ + 'ts-node', + scriptPath, + '--run-dir', + runDir, + '--base-url', + baseUrl, + '--tenant-count', + String(dbpmTenantCount), + '--user-password', + dbpmUserPassword, + '--user-prefix', + dbpmUserPrefix, + '--shape-variants', + String(dbpmShapeVariants), + '--route-host', + dbpmRouteHost, + '--private-api-name', + dbpmPrivateApiName, + '--private-database-id', + dbpmPrivateDatabaseId, + ...(dbpmAuthHost ? ['--auth-host', dbpmAuthHost] : []), + ...(dbpmProvisionHost ? ['--provision-host', dbpmProvisionHost] : []), + '--pg-host', + String(pgConfig.host), + '--pg-port', + String(pgConfig.port), + '--pg-database', + String(pgConfig.database), + '--pg-user', + String(pgConfig.user), + '--pg-password', + String(pgConfig.password), + ], + { stdio: ['ignore', 'pipe', 'pipe'] }, + ); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (chunk) => { + stdout += String(chunk); + }); + child.stderr.on('data', (chunk) => { + stderr += String(chunk); + }); + + child.on('close', (code) => { + resolve({ + code, + ok: code === 0, + stdout: stdout.trim(), + stderr: stderr.trim(), + }); + }); + }); +}; + +const runBusinessProfileBuilder = async () => { + const scriptPath = path.join(__dirname, 'build-business-op-profiles.ts'); + + return await new Promise((resolve) => { + const child = spawn( + 'npx', + [ + 'ts-node', + scriptPath, + '--run-dir', + runDir, + '--base-url', + baseUrl, + '--manifest', + businessManifestPath, + '--tokens', + tokenOutputPath, + '--output', + businessProfilesOutputPath, + '--routing-mode', + businessRoutingMode, + '--compat-routing-mode', + businessCompatRoutingMode, + '--public-api-name', + businessPublicApiName, + '--public-subdomain-prefix', + businessPublicSubdomainPrefix, + ], + { stdio: ['ignore', 'pipe', 'pipe'] }, + ); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (chunk) => { + stdout += String(chunk); + }); + child.stderr.on('data', (chunk) => { + stderr += String(chunk); + }); + + child.on('close', (code) => { + resolve({ + code, + ok: code === 0, + stdout: stdout.trim(), + stderr: stderr.trim(), + }); + }); + }); +}; + +const runKeyspaceBuilder = async () => { + const scriptPath = path.join(__dirname, 'build-keyspace-profiles.ts'); + + const cmd = [ + 'ts-node', + scriptPath, + '--run-dir', + runDir, + '--base-url', + baseUrl, + '--input', + tokenOutputPath, + '--output', + keyspaceOutputPath, + '--mode', + keyspaceMode, + '--target-route-keys', + String(keyspaceTargetRouteKeys), + '--max-profiles', + String(keyspaceMaxProfiles), + '--max-schema-width', + String(keyspaceMaxSchemaWidth), + '--schema-pool', + keyspaceSchemaPool, + ]; + + if (keyspaceNoSmokeCheck) { + cmd.push('--no-smoke-check'); + } + + return await new Promise((resolve) => { + const child = spawn('npx', cmd, { + stdio: ['ignore', 'pipe', 'pipe'], + }); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (chunk) => { + stdout += String(chunk); + }); + child.stderr.on('data', (chunk) => { + stderr += String(chunk); + }); + + child.on('close', (code) => { + resolve({ + code, + ok: code === 0, + stdout: stdout.trim(), + stderr: stderr.trim(), + }); + }); + }); +}; + +const main = async () => { + const dirs = await ensureRunDirs(runDir); + const pool = new Pool(pgConfig); + + const startedAt = new Date(); + const report = { + startedAt: startedAt.toISOString(), + runId, + runDir, + baseUrl, + pg: { + host: pgConfig.host, + port: pgConfig.port, + database: pgConfig.database, + user: pgConfig.user, + }, + readiness: { + phase1AReady: false, + phase1BReady: false, + phase1CReady: false, + phase1Ready: false, + tenantReadyForPhase2: false, + }, + checks: {}, + warnings: [], + errors: [], + }; + + try { + let phase1BReady = false; + let phase1CReady = false; + + const [memoryCheck, dbCheck] = await Promise.all([ + getJson({ url: `${baseUrl}/debug/memory` }), + getJson({ url: `${baseUrl}/debug/db` }), + ]); + + report.checks.debugMemory = { + ok: memoryCheck.ok, + status: memoryCheck.status, + elapsedMs: memoryCheck.elapsedMs, + pid: memoryCheck.json?.pid ?? null, + error: memoryCheck.error ?? null, + }; + + report.checks.debugDb = { + ok: dbCheck.ok, + status: dbCheck.status, + elapsedMs: dbCheck.elapsedMs, + hasPool: !!dbCheck.json?.pool, + error: dbCheck.error ?? null, + }; + + const [tenantBaselineResult, apiCandidatesResult] = await Promise.all([ + pool.query(queries.tenantBaseline), + pool.query(queries.apiCandidates), + ]); + + const tenantBaseline = tenantBaselineResult.rows[0] ?? null; + const profiles = buildProfiles(apiCandidatesResult.rows ?? []); + const smoke = await smokeCheckGraphql({ baseUrl, profiles }); + + const healthyProfiles = smoke.filter((item) => item.ok); + const healthyProfileKeys = new Set(healthyProfiles.map((item) => item.profileKey)); + const readyProfiles = profiles.filter((profile) => healthyProfileKeys.has(profile.key)); + + report.checks.tenantBaseline = tenantBaseline; + report.checks.routingProfiles = { + discovered: profiles.length, + healthy: readyProfiles.length, + unhealthy: profiles.length - readyProfiles.length, + smoke, + }; + + const orgCount = Number(tenantBaseline?.org_count ?? 0); + const orgScaleReady = orgCount >= requireTenants; + + if (!runDbpmTechValidation && !orgScaleReady) { + const msg = `tenant scale insufficient: org_count=${orgCount} < min=${requireTenants}`; + if (enforceTenantScale) { + report.errors.push(msg); + } else { + report.warnings.push(`${msg} (allow-underprovisioned enabled)`); + } + } else if (!orgScaleReady) { + report.warnings.push( + `org_count (${orgCount}) is below min-tenant-count (${requireTenants}); DBPM technical validation path will provide tenant scale gate.`, + ); + } + + if (!report.checks.debugMemory.ok) { + report.errors.push('/debug/memory is not reachable.'); + } + if (!report.checks.debugDb.ok) { + report.errors.push('/debug/db is not reachable.'); + } + if (readyProfiles.length === 0) { + report.errors.push('No healthy GraphQL request profile found for this server/database config.'); + } + + report.readiness.phase1AReady = report.errors.length === 0; + + await writeJson(path.join(dirs.dataDir, 'request-profiles.discovered.json'), profiles); + await writeJson(path.join(dirs.dataDir, 'request-profiles.ready.json'), readyProfiles); + + if (runDbpmTechValidation && report.errors.length === 0) { + const dbpmTechValidation = await runDbpmTechValidationScript(); + report.checks.dbpmTechValidation = dbpmTechValidation; + + if (!dbpmTechValidation.ok) { + report.errors.push( + `DBPM tech validation failed (exit=${dbpmTechValidation.code}). stderr=${dbpmTechValidation.stderr || 'none'}`, + ); + } else if (!(await pathExists(dbTechValidationPath))) { + report.errors.push(`DBPM tech validation report not found: ${dbTechValidationPath}`); + } else if (!(await pathExists(businessManifestPath))) { + report.errors.push(`Business manifest not found: ${businessManifestPath}`); + } else { + const dbTechPayload = JSON.parse(await fs.readFile(dbTechValidationPath, 'utf8')); + const manifestPayload = JSON.parse(await fs.readFile(businessManifestPath, 'utf8')); + const manifestRows = Array.isArray(manifestPayload) ? manifestPayload : []; + const manifestTenantCount = manifestRows.length; + + report.checks.dbpmValidation = { + reportPath: dbTechValidationPath, + manifestPath: businessManifestPath, + requestedTenants: Number(dbTechPayload?.summary?.requestedTenants ?? dbpmTenantCount), + successTenants: Number(dbTechPayload?.summary?.successTenants ?? manifestTenantCount), + failedTenants: Number(dbTechPayload?.summary?.failedTenants ?? 0), + passed: !!dbTechPayload?.summary?.passed, + manifestTenantCount, + minTenantCount: requireTenants, + }; + + const dbpmScaleReady = manifestTenantCount >= requireTenants; + report.readiness.tenantReadyForPhase2 = dbpmScaleReady; + + if (!dbpmScaleReady) { + const msg = `DBPM manifest tenant count insufficient: manifestTenantCount=${manifestTenantCount} < min=${requireTenants}`; + if (enforceTenantScale) { + report.errors.push(msg); + } else { + report.warnings.push(`${msg} (allow-underprovisioned enabled)`); + } + } + } + } + + const hasCredentials = await pathExists(credentialsPath); + if (!hasCredentials) { + if (!bootstrapDefaultCredential) { + report.errors.push( + `Token phase requires credentials file but it does not exist: ${credentialsPath}`, + ); + } else { + const credentials = buildBootstrapCredentials({ readyProfiles }); + if (credentials.length === 0) { + report.errors.push( + 'Cannot bootstrap credentials because no healthy private-header routing profile is available.', + ); + } else { + await writeJson(credentialsPath, credentials); + report.warnings.push( + `No credentials file provided; bootstrapped ${credentials.length} credential rows using default local admin credential.`, + ); + } + } + } + + if (report.errors.length === 0) { + const tokenBuild = await runTokenBuilder(); + report.checks.tokenBuild = tokenBuild; + + if (!tokenBuild.ok) { + report.errors.push( + `Token pool build failed (exit=${tokenBuild.code}). stderr=${tokenBuild.stderr || 'none'}`, + ); + } else if (!(await pathExists(tokenOutputPath))) { + report.errors.push(`Token pool output not found: ${tokenOutputPath}`); + } else { + const tokenPayload = JSON.parse(await fs.readFile(tokenOutputPath, 'utf8')); + const profilesFromToken = Array.isArray(tokenPayload?.profiles) ? tokenPayload.profiles : []; + const distinctTenantKeys = new Set( + profilesFromToken.map((row) => row.tenantKey || row.key).filter(Boolean), + ); + + const successCount = Number(tokenPayload?.successCount ?? profilesFromToken.length); + const failureCount = Number(tokenPayload?.failureCount ?? 0); + + report.checks.tokenPool = { + credentialsPath, + outputPath: tokenOutputPath, + successCount, + failureCount, + distinctTenantKeys: distinctTenantKeys.size, + minTokenTenants, + recommendedTokenTenants, + }; + + report.readiness.tenantReadyForPhase2 = distinctTenantKeys.size >= requireTenants; + + if (successCount <= 0) { + report.errors.push('Token pool generated zero usable token profiles.'); + } + + if (distinctTenantKeys.size < minTokenTenants) { + report.errors.push( + `Token coverage insufficient: distinctTenantKeys=${distinctTenantKeys.size} < minTokenTenants=${minTokenTenants}`, + ); + } + + if (distinctTenantKeys.size < recommendedTokenTenants) { + report.warnings.push( + `Token coverage is below recommended scale: distinctTenantKeys=${distinctTenantKeys.size}, recommended=${recommendedTokenTenants}`, + ); + } + + if (failureCount > 0) { + report.warnings.push( + `Token pool had partial credential failures: failureCount=${failureCount}.`, + ); + } + + phase1BReady = report.errors.length === 0; + + if (phase1BReady) { + const keyspaceBuild = await runKeyspaceBuilder(); + report.checks.keyspaceBuild = keyspaceBuild; + + if (!keyspaceBuild.ok) { + report.errors.push( + `Keyspace build failed (exit=${keyspaceBuild.code}). stderr=${keyspaceBuild.stderr || 'none'}`, + ); + } else if (!(await pathExists(keyspaceOutputPath))) { + report.errors.push(`Keyspace output not found: ${keyspaceOutputPath}`); + } else { + const keyspacePayload = JSON.parse(await fs.readFile(keyspaceOutputPath, 'utf8')); + const selectedRouteKeys = Number( + keyspacePayload?.selectedRouteKeys ?? keyspacePayload?.selectedRoutes?.length ?? 0, + ); + const totalOutputProfiles = Number( + keyspacePayload?.totalOutputProfiles ?? keyspacePayload?.profiles?.length ?? 0, + ); + const totalInputProfiles = Number( + keyspacePayload?.totalInputProfiles ?? profilesFromToken.length, + ); + + report.checks.keyspace = { + inputPath: tokenOutputPath, + outputPath: keyspaceOutputPath, + mode: keyspaceMode, + targetRouteKeys: keyspaceTargetRouteKeys, + minRouteKeys: keyspaceMinRouteKeys, + selectedRouteKeys, + totalInputProfiles, + totalOutputProfiles, + noSmokeCheck: keyspaceNoSmokeCheck, + }; + + if (selectedRouteKeys < keyspaceMinRouteKeys) { + report.errors.push( + `Keyspace coverage insufficient: selectedRouteKeys=${selectedRouteKeys} < minRouteKeys=${keyspaceMinRouteKeys}`, + ); + } + + if (totalOutputProfiles <= 0) { + report.errors.push('Keyspace builder produced zero output profiles.'); + } + + if (selectedRouteKeys < keyspaceTargetRouteKeys) { + report.warnings.push( + `Keyspace route keys below target: selected=${selectedRouteKeys}, target=${keyspaceTargetRouteKeys}`, + ); + } + + const businessProfilesBuild = await runBusinessProfileBuilder(); + report.checks.businessProfilesBuild = businessProfilesBuild; + + if (!businessProfilesBuild.ok) { + report.errors.push( + `Business op profile build failed (exit=${businessProfilesBuild.code}). stderr=${businessProfilesBuild.stderr || 'none'}`, + ); + } else if (!(await pathExists(businessProfilesOutputPath))) { + report.errors.push(`Business op profile output not found: ${businessProfilesOutputPath}`); + } else { + const businessPayload = JSON.parse( + await fs.readFile(businessProfilesOutputPath, 'utf8'), + ); + const businessProfiles = Array.isArray(businessPayload?.profiles) + ? businessPayload.profiles + : []; + const businessTenantKeys = new Set( + businessProfiles.map((profile) => profile.tenantKey || profile.key).filter(Boolean), + ); + + report.checks.businessProfiles = { + outputPath: businessProfilesOutputPath, + successCount: Number(businessPayload?.successCount ?? businessProfiles.length), + failureCount: Number(businessPayload?.failureCount ?? 0), + distinctTenantKeys: businessTenantKeys.size, + }; + + if (businessProfiles.length === 0) { + report.errors.push('Business op profile builder produced zero profiles.'); + } + } + } + } + } + } + + phase1CReady = report.errors.length === 0 && !!report.checks.keyspace?.outputPath; + report.readiness.phase1BReady = phase1BReady; + report.readiness.phase1CReady = phase1CReady; + report.readiness.phase1Ready = + report.readiness.phase1AReady && + report.readiness.phase1BReady && + report.readiness.phase1CReady; + + await writeJson(path.join(dirs.reportsDir, 'preflight.json'), report); + + const summary = { + runDir, + phase1AReady: report.readiness.phase1AReady, + phase1BReady: report.readiness.phase1BReady, + phase1CReady: report.readiness.phase1CReady, + phase1Ready: report.readiness.phase1Ready, + tenantReadyForPhase2: report.readiness.tenantReadyForPhase2, + discoveredProfiles: report.checks.routingProfiles?.discovered ?? 0, + healthyProfiles: report.checks.routingProfiles?.healthy ?? 0, + tokenSuccessCount: report.checks.tokenPool?.successCount ?? 0, + tokenDistinctTenants: report.checks.tokenPool?.distinctTenantKeys ?? 0, + keyspaceRouteKeys: report.checks.keyspace?.selectedRouteKeys ?? 0, + keyspaceOutputProfiles: report.checks.keyspace?.totalOutputProfiles ?? 0, + warnings: report.warnings.length, + errors: report.errors.length, + }; + + console.log(JSON.stringify(summary, null, 2)); + + if (!report.readiness.phase1Ready) { + process.exit(2); + } + if (enforceTenantScale && !report.readiness.tenantReadyForPhase2) { + process.exit(3); + } + } finally { + await pool.end(); + } +}; + +main().catch((error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exit(1); +}); diff --git a/graphql/server/perf/src/legacy/phase1-tech-validate-dbpm.ts b/graphql/server/perf/src/legacy/phase1-tech-validate-dbpm.ts new file mode 100644 index 0000000000..b979480d92 --- /dev/null +++ b/graphql/server/perf/src/legacy/phase1-tech-validate-dbpm.ts @@ -0,0 +1,786 @@ +#!/usr/bin/env node +// @ts-nocheck + +import path from 'node:path'; +import { Pool } from 'pg'; + +import { + DEFAULT_BASE_URL, + DEFAULT_TMP_ROOT, + ensureRunDirs, + getArgValue, + makeRunId, + postJson, + sleep, + writeJson, +} from './common'; + +const args = process.argv.slice(2); + +const runId = getArgValue(args, '--run-id', makeRunId('tech-validate-dbpm')); +const runDir = path.resolve( + getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)), +); +const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL); + +const tenantCount = Number.parseInt(getArgValue(args, '--tenant-count', '2'), 10); +const userPassword = getArgValue(args, '--user-password', 'Constructive!23456'); +const userPrefix = getArgValue(args, '--user-prefix', `dbpm-tech-${Date.now()}`); +const provisionDomain = getArgValue(args, '--provision-domain', 'localhost'); +const modulesArg = getArgValue(args, '--provision-modules', 'all'); +const targetSchemaName = getArgValue(args, '--target-schema-name', 'app_public'); +const tablePrefix = getArgValue(args, '--table-prefix', 'items_dbpm'); +const shapeVariantCount = Number.parseInt( + getArgValue(args, '--shape-variants', '0'), + 10, +); + +const provisionTimeoutMs = Number.parseInt( + getArgValue(args, '--provision-timeout-ms', '180000'), + 10, +); +const provisionPollMs = Number.parseInt(getArgValue(args, '--provision-poll-ms', '2000'), 10); + +const routeHost = getArgValue(args, '--route-host', 'localhost'); +const privateApiName = getArgValue(args, '--private-api-name', 'private'); +const privateDatabaseId = getArgValue( + args, + '--private-database-id', + '028752cb-510b-1438-2f39-64534bd1cbd7', +); +const authHost = getArgValue(args, '--auth-host', ''); +const provisionHost = getArgValue(args, '--provision-host', ''); + +const privateRouteHeaders = { + Host: routeHost, + 'X-Api-Name': privateApiName, + 'X-Database-Id': privateDatabaseId, +}; +const authRouteHeaders = authHost ? { Host: authHost } : privateRouteHeaders; +const provisionRouteHeaders = provisionHost ? { Host: provisionHost } : privateRouteHeaders; + +const modules = modulesArg + .split(',') + .map((item) => item.trim()) + .filter(Boolean); + +if (modules.length === 0) { + throw new Error(`Invalid --provision-modules: ${modulesArg}`); +} + +// --------------------------------------------------------------------------- +// Shape variant definitions (Option A — extra provisioned tables only) +// --------------------------------------------------------------------------- + +const fieldType = (name) => ({ name }); +const provisionField = (name, typeName) => ({ name, type: fieldType(typeName) }); + +/** + * Each entry describes extra tables to provision for tenants assigned to + * that variant group. Group 0 always means "main table only" (no extras). + * + * The provisioning mutation is the same `createSecureTableProvision` used + * for the main business table — this guarantees RLS, grants, policies, + * and PostGraphile type generation are all exercised. + */ +const VARIANT_DEFS = [ + // Group 0: base case — no extra tables + { tables: [] }, + // Group 1: extra table with 2 columns (tags) + { + tables: [ + { + suffix: 'tags', + fields: [ + provisionField('label', 'text'), + provisionField('priority', 'integer'), + ], + }, + ], + }, + // Group 2: extra table with 3 columns (metrics) + { + tables: [ + { + suffix: 'metrics', + fields: [ + provisionField('value', 'numeric'), + provisionField('recorded_at', 'timestamptz'), + provisionField('active', 'boolean'), + ], + }, + ], + }, +]; + +const effectiveVariantCount = Math.min( + Math.max(shapeVariantCount, 0), + VARIANT_DEFS.length, +); + +const pgConfig = { + host: getArgValue(args, '--pg-host', process.env.PGHOST || 'localhost'), + port: Number.parseInt(getArgValue(args, '--pg-port', process.env.PGPORT || '5432'), 10), + database: getArgValue(args, '--pg-database', process.env.PGDATABASE || 'constructive'), + user: getArgValue(args, '--pg-user', process.env.PGUSER || 'postgres'), + password: getArgValue(args, '--pg-password', process.env.PGPASSWORD || 'password'), +}; + +const signUpMutation = ` +mutation SignUp($input: SignUpInput!) { + signUp(input: $input) { + result { + id + userId + accessToken + } + } +} +`; + +const signInMutation = ` +mutation SignIn($input: SignInInput!) { + signIn(input: $input) { + result { + id + userId + accessToken + } + } +} +`; + +const createDatabaseProvisionModuleMutation = ` +mutation CreateDatabaseProvisionModule($input: CreateDatabaseProvisionModuleInput!) { + createDatabaseProvisionModule(input: $input) { + databaseProvisionModule { + id + databaseName + ownerId + domain + modules + status + errorMessage + databaseId + createdAt + completedAt + } + } +} +`; + +const createSecureTableProvisionMutation = ` +mutation CreateSecureTableProvision($input: CreateSecureTableProvisionInput!) { + createSecureTableProvision(input: $input) { + secureTableProvision { + id + databaseId + schemaId + tableId + tableName + outFields + } + } +} +`; + +const gql = async ({ query, variables, headers = {}, routeHeaders = provisionRouteHeaders, timeoutMs = 30000 }) => { + return await postJson({ + url: `${baseUrl}/graphql`, + headers: { + ...routeHeaders, + ...headers, + }, + payload: { query, variables }, + timeoutMs, + }); +}; + +const firstError = (response) => { + const message = response.json?.errors?.[0]?.message || response.error; + if (message) return message; + const text = typeof response.text === 'string' ? response.text.trim().slice(0, 300) : ''; + if (text) return `status=${response.status}; body=${text}`; + return `status=${response.status}; empty response`; +}; + +const signUpOrSignInUser = async ({ email, password }) => { + const signUpRes = await gql({ + query: signUpMutation, + routeHeaders: authRouteHeaders, + variables: { + input: { + email, + password, + rememberMe: true, + }, + }, + }); + + const signUpResult = signUpRes.json?.data?.signUp?.result; + if (signUpRes.ok && signUpResult?.accessToken && signUpResult?.userId) { + return { + mode: 'signUp', + userId: signUpResult.userId, + tokenId: signUpResult.id, + accessToken: signUpResult.accessToken, + }; + } + + const signInRes = await gql({ + query: signInMutation, + routeHeaders: authRouteHeaders, + variables: { + input: { + email, + password, + rememberMe: true, + }, + }, + }); + + const signInResult = signInRes.json?.data?.signIn?.result; + if (signInRes.ok && signInResult?.accessToken && signInResult?.userId) { + return { + mode: 'signIn', + userId: signInResult.userId, + tokenId: signInResult.id, + accessToken: signInResult.accessToken, + }; + } + + throw new Error( + `Auth failed for ${email}; signUpError=${firstError(signUpRes)}; signInError=${firstError(signInRes)}`, + ); +}; + +const createProvisionModule = async ({ token, ownerId, databaseName, subdomain }) => { + const response = await gql({ + query: createDatabaseProvisionModuleMutation, + headers: { + Authorization: `Bearer ${token}`, + }, + variables: { + input: { + databaseProvisionModule: { + databaseName, + ownerId, + domain: provisionDomain, + subdomain, + modules, + options: {}, + bootstrapUser: true, + }, + }, + }, + timeoutMs: provisionTimeoutMs, + }); + + const record = response.json?.data?.createDatabaseProvisionModule?.databaseProvisionModule; + if (!response.ok || !record?.id) { + throw new Error( + `createDatabaseProvisionModule failed db=${databaseName}; status=${response.status}; error=${firstError(response)}`, + ); + } + + return record; +}; + +const waitForProvisionCompletion = async ({ pool, moduleId }) => { + const started = Date.now(); + + while (Date.now() - started < provisionTimeoutMs) { + const result = await pool.query( + ` + select + id::text, + database_name, + owner_id::text, + status, + error_message, + database_id::text, + modules::text, + created_at, + updated_at, + completed_at + from metaschema_modules_public.database_provision_module + where id = $1::uuid + `, + [moduleId], + ); + + const row = result.rows[0]; + if (!row) { + throw new Error(`Provision module not found: ${moduleId}`); + } + + if (row.status === 'failed') { + throw new Error( + `Provision failed moduleId=${moduleId}; error=${row.error_message || 'no error_message'}`, + ); + } + + if (row.status === 'completed' && row.database_id) { + return row; + } + + await sleep(provisionPollMs); + } + + throw new Error(`Provision timeout moduleId=${moduleId}; timeoutMs=${provisionTimeoutMs}`); +}; + +const findTargetSchema = async ({ pool, databaseId, targetName }) => { + const exact = await pool.query( + ` + select id::text, name, schema_name + from metaschema_public.schema + where database_id = $1::uuid + and name = $2 + order by created_at desc + limit 1 + `, + [databaseId, targetName], + ); + + if (exact.rows[0]) { + return exact.rows[0]; + } + + const fallback = await pool.query( + ` + select id::text, name, schema_name + from metaschema_public.schema + where database_id = $1::uuid + and name like 'app_%' + order by created_at desc + limit 1 + `, + [databaseId], + ); + + if (fallback.rows[0]) { + return fallback.rows[0]; + } + + throw new Error(`No app schema found for databaseId=${databaseId}`); +}; + +const createBusinessTable = async ({ token, databaseId, schemaId, tableName }) => { + const response = await gql({ + query: createSecureTableProvisionMutation, + headers: { + Authorization: `Bearer ${token}`, + }, + variables: { + input: { + secureTableProvision: { + databaseId, + schemaId, + tableName, + nodes: ['DataId'], + fields: [provisionField('note', 'text')], + }, + }, + }, + }); + + const record = response.json?.data?.createSecureTableProvision?.secureTableProvision; + if (!response.ok || !record?.id) { + throw new Error( + `createSecureTableProvision failed db=${databaseId} schema=${schemaId}; status=${response.status}; error=${firstError(response)}`, + ); + } + + return record; +}; + +/** + * Provision a variant table via the same mutation used for the main + * business table. Failures are non-fatal — logged and recorded but + * do not abort the tenant. + */ +const createVariantTable = async ({ token, databaseId, schemaId, tableName, fields }) => { + const response = await gql({ + query: createSecureTableProvisionMutation, + headers: { + Authorization: `Bearer ${token}`, + }, + variables: { + input: { + secureTableProvision: { + databaseId, + schemaId, + tableName, + nodes: ['DataId'], + fields, + }, + }, + }, + }); + + const record = response.json?.data?.createSecureTableProvision?.secureTableProvision; + if (!response.ok || !record?.id) { + return { + ok: false, + error: `createSecureTableProvision(variant) failed table=${tableName}; status=${response.status}; error=${firstError(response)}`, + }; + } + + return { ok: true, record }; +}; + +const quoteIdent = (value) => `"${String(value).replace(/"/g, '""')}"`; + +const validateSqlOps = async ({ pool, physicalSchema, tableName, tenantIndex }) => { + const columnsResult = await pool.query( + ` + select column_name, data_type + from information_schema.columns + where table_schema = $1 + and table_name = $2 + order by ordinal_position + `, + [physicalSchema, tableName], + ); + + const columns = columnsResult.rows; + const hasId = columns.some((col) => col.column_name === 'id' && col.data_type === 'uuid'); + const hasNote = columns.some((col) => col.column_name === 'note' && col.data_type === 'text'); + if (!hasId || !hasNote) { + throw new Error( + `Unexpected columns ${physicalSchema}.${tableName}; expected id(uuid)+note(text), got=${JSON.stringify(columns)}`, + ); + } + + const qualifiedTable = `${quoteIdent(physicalSchema)}.${quoteIdent(tableName)}`; + + const insert = await pool.query( + `insert into ${qualifiedTable} (note) values ($1) returning id::text as id, note`, + [`hello-from-dbpm-tenant-${tenantIndex}`], + ); + const inserted = insert.rows[0]; + + const select = await pool.query( + `select count(*)::int as c from ${qualifiedTable} where id = $1::uuid`, + [inserted.id], + ); + + const update = await pool.query( + `update ${qualifiedTable} set note = $2 where id = $1::uuid returning id::text as id, note`, + [inserted.id, `updated-dbpm-tenant-${tenantIndex}`], + ); + + return { + columns, + insertRow: inserted, + selectCount: select.rows[0]?.c ?? 0, + updateRow: update.rows[0] || null, + }; +}; + +const listDatabaseSchemas = async ({ pool, databaseId }) => { + const result = await pool.query( + ` + select name, schema_name + from metaschema_public.schema + where database_id = $1::uuid + order by name + `, + [databaseId], + ); + return result.rows.map((row) => ({ + name: row.name, + schemaName: row.schema_name, + })); +}; + +const main = async () => { + if (!Number.isFinite(tenantCount) || tenantCount < 2) { + throw new Error(`--tenant-count must be >= 2, got: ${tenantCount}`); + } + + const dirs = await ensureRunDirs(runDir); + const pool = new Pool(pgConfig); + + const accounts = []; + const failures = []; + + try { + for (let i = 1; i <= tenantCount; i += 1) { + const idx = String(i).padStart(2, '0'); + const suffix = `${Date.now().toString().slice(-6)}-${i}`; + const email = `${userPrefix}-${idx}@example.com`; + const databaseName = `perf_dbpm_${suffix.replace(/-/g, '_')}`; + const subdomain = `dbpm-${suffix}`; + const tableName = `${tablePrefix}_${suffix.replace(/-/g, '_')}`; + + try { + const auth = await signUpOrSignInUser({ email, password: userPassword }); + const provision = await createProvisionModule({ + token: auth.accessToken, + ownerId: auth.userId, + databaseName, + subdomain, + }); + + const provisionFinal = await waitForProvisionCompletion({ + pool, + moduleId: provision.id, + }); + + const schema = await findTargetSchema({ + pool, + databaseId: provisionFinal.database_id, + targetName: targetSchemaName, + }); + + const secureTable = await createBusinessTable({ + token: auth.accessToken, + databaseId: provisionFinal.database_id, + schemaId: schema.id, + tableName, + }); + + // --- Option A: shape variant tables --- + const variantResults = []; + const variantIndex = effectiveVariantCount > 0 + ? (i - 1) % effectiveVariantCount + : 0; + const variantDef = effectiveVariantCount > 0 + ? VARIANT_DEFS[variantIndex] + : VARIANT_DEFS[0]; + + for (const vtDef of variantDef.tables) { + const vtName = `${tablePrefix}_variant_${vtDef.suffix}_${suffix.replace(/-/g, '_')}`; + console.log(` [tenant ${i}] provisioning variant table: ${vtName} (group ${variantIndex}, fields=${vtDef.fields.length})`); + const vtResult = await createVariantTable({ + token: auth.accessToken, + databaseId: provisionFinal.database_id, + schemaId: schema.id, + tableName: vtName, + fields: vtDef.fields, + }); + if (vtResult.ok) { + variantResults.push({ + tableName: vtResult.record.tableName || vtName, + tableId: vtResult.record.tableId, + fields: vtDef.fields, + suffix: vtDef.suffix, + }); + } else { + console.warn(` [tenant ${i}] variant table failed: ${vtResult.error}`); + variantResults.push({ + tableName: vtName, + fields: vtDef.fields, + suffix: vtDef.suffix, + error: vtResult.error, + }); + } + } + + const databaseSchemas = await listDatabaseSchemas({ + pool, + databaseId: provisionFinal.database_id, + }); + + const sqlValidation = await validateSqlOps({ + pool, + physicalSchema: schema.schema_name, + tableName: secureTable.tableName || tableName, + tenantIndex: i, + }); + + accounts.push({ + tenantKey: `user:${auth.userId}`, + email, + authMode: auth.mode, + authUserId: auth.userId, + shapeVariant: { + index: variantIndex, + tables: variantResults, + }, + created: { + databaseProvisionModule: { + id: provision.id, + databaseName: provision.databaseName, + ownerId: provision.ownerId, + domain: provision.domain, + modules: provision.modules, + status: provision.status, + databaseId: provision.databaseId, + createdAt: provision.createdAt, + completedAt: provision.completedAt, + }, + provisionFinal: { + status: provisionFinal.status, + databaseId: provisionFinal.database_id, + modules: provisionFinal.modules, + completedAt: provisionFinal.completed_at, + }, + schema: { + id: schema.id, + name: schema.name, + schemaName: schema.schema_name, + availableSchemas: databaseSchemas, + }, + secureTableProvision: { + id: secureTable.id, + databaseId: secureTable.databaseId, + schemaId: secureTable.schemaId, + tableId: secureTable.tableId, + tableName: secureTable.tableName, + outFields: secureTable.outFields, + }, + }, + sqlValidation: { + physicalSchema: schema.schema_name, + physicalTable: `${schema.schema_name}.${secureTable.tableName || tableName}`, + ...sqlValidation, + }, + }); + } catch (error) { + failures.push({ + tenantIndex: i, + email, + error: error instanceof Error ? error.message : String(error), + }); + } + } + } finally { + await pool.end(); + } + + // --- Shape variant success/failure accounting --- + let variantTablesExpected = 0; + let variantTablesSucceeded = 0; + let variantTablesFailed = 0; + let tenantsWithVariantFailures = 0; + + for (const account of accounts) { + const sv = account.shapeVariant; + // Only count tenants that were supposed to get extra tables (group > 0) + const expectedForTenant = sv.tables.length; + if (expectedForTenant === 0 && sv.index === 0) { + // Group 0 = no extras expected — not a failure + continue; + } + const failedForTenant = sv.tables.filter((t) => t.error).length; + variantTablesExpected += expectedForTenant; + variantTablesSucceeded += expectedForTenant - failedForTenant; + variantTablesFailed += failedForTenant; + if (failedForTenant > 0) { + tenantsWithVariantFailures += 1; + } + } + + const variantsRequested = effectiveVariantCount > 0; + const variantsPassed = !variantsRequested || variantTablesFailed === 0; + + const summary = { + requestedTenants: tenantCount, + successTenants: accounts.length, + failedTenants: failures.length, + passed: accounts.length >= 2 && failures.length === 0 && variantsPassed, + ...(variantsRequested + ? { + shapeVariants: { + requested: true, + variantGroupCount: effectiveVariantCount, + variantTablesExpected, + variantTablesSucceeded, + variantTablesFailed, + tenantsWithVariantFailures, + passed: variantsPassed, + }, + } + : {}), + }; + + if (variantsRequested && !variantsPassed) { + console.error( + `\n⚠ Shape variant provisioning incomplete: ${variantTablesFailed}/${variantTablesExpected} expected variant tables failed across ${tenantsWithVariantFailures} tenant(s).\n` + + ` The run is marked as FAILED because --shape-variants ${shapeVariantCount} was requested but structural divergence was not fully achieved.\n`, + ); + } + + const report = { + createdAt: new Date().toISOString(), + baseUrl, + routes: { + auth: authRouteHeaders, + provision: provisionRouteHeaders, + fallbackPrivate: privateRouteHeaders, + }, + flow: 'signUp/signIn -> createDatabaseProvisionModule(modules=all) -> use app_public -> createSecureTableProvision(DataId+note) -> SQL insert/select/update by id', + options: { + tenantCount, + modules, + provisionDomain, + targetSchemaName, + tablePrefix, + provisionTimeoutMs, + provisionPollMs, + shapeVariantCount: effectiveVariantCount, + }, + summary, + accounts, + failures, + }; + + const manifest = accounts.map((account) => ({ + tenantKey: account.tenantKey, + email: account.email, + databaseId: account.created.provisionFinal.databaseId, + schemaId: account.created.schema.id, + schemaName: account.created.schema.name, + physicalSchema: account.created.schema.schemaName, + availableSchemas: account.created.schema.availableSchemas, + tableId: account.created.secureTableProvision.tableId, + tableName: account.created.secureTableProvision.tableName, + seedRowId: account.sqlValidation.insertRow?.id ?? null, + variantIndex: account.shapeVariant.index, + variantTables: account.shapeVariant.tables, + })); + + const credentials = accounts.map((account) => ({ + tenantKey: account.tenantKey, + email: account.email, + password: userPassword, + host: authRouteHeaders.Host, + ...(authRouteHeaders['X-Api-Name'] ? { apiName: authRouteHeaders['X-Api-Name'] } : {}), + databaseId: account.created.provisionFinal.databaseId, + provisionedDatabaseId: account.created.provisionFinal.databaseId, + })); + + const reportPath = path.join(dirs.reportsDir, 'db-tech-validation.json'); + const manifestPath = path.join(dirs.dataDir, 'business-table-manifest.json'); + const credentialsPath = path.join(dirs.dataDir, 'tenant-credentials.json'); + + await writeJson(reportPath, report); + await writeJson(manifestPath, manifest); + await writeJson(credentialsPath, credentials); + + console.log( + JSON.stringify( + { + runDir, + reportPath, + manifestPath, + credentialsPath, + summary, + }, + null, + 2, + ), + ); + + if (!summary.passed) { + process.exitCode = 1; + } +}; + +main().catch((error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exit(1); +}); diff --git a/graphql/server/perf/src/legacy/phase2-load.ts b/graphql/server/perf/src/legacy/phase2-load.ts new file mode 100644 index 0000000000..7e5d255407 --- /dev/null +++ b/graphql/server/perf/src/legacy/phase2-load.ts @@ -0,0 +1,1476 @@ +#!/usr/bin/env node +// @ts-nocheck + +import fsSync from 'node:fs'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { spawn } from 'node:child_process'; + +import { + DEFAULT_BASE_URL, + DEFAULT_TMP_ROOT, + getArgValue, + getJson, + parseIntArg, + postJson, + sleep, + writeJson, +} from './common'; +import { + buildTargetsFromProfiles, + ensurePublicAccessForTargets, + getUnsafeTargets, +} from './public-test-access-lib'; + +const args = process.argv.slice(2); + +const runDir = path.resolve( + getArgValue( + args, + '--run-dir', + path.join(DEFAULT_TMP_ROOT, getArgValue(args, '--run-id', 'graphile-cache-leak-manual-run')), + ), +); +const dirs = { + runDir, + logsDir: path.join(runDir, 'logs'), + samplerDir: path.join(runDir, 'logs', 'sampler'), + heapDir: path.join(runDir, 'logs', 'heap'), + dataDir: path.join(runDir, 'data'), + reportsDir: path.join(runDir, 'reports'), + tmpScriptsDir: path.join(runDir, 'tmp-scripts'), +}; +for (const dir of Object.values(dirs)) { + fsSync.mkdirSync(dir, { recursive: true }); +} + +const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL); +const workers = parseIntArg(getArgValue(args, '--workers', '16'), 16); +const durationSeconds = parseIntArg(getArgValue(args, '--duration-seconds', '1200'), 1200); +const idleSeconds = parseIntArg(getArgValue(args, '--idle-seconds', '45'), 45); +const requireTenants = parseIntArg(getArgValue(args, '--min-tenant-count', '10'), 10); +const enforceTenantScale = !args.includes('--allow-underprovisioned'); +const tier = getArgValue(args, '--tier', 'tier-default'); +const hotRatio = Number.parseFloat(getArgValue(args, '--hot-ratio', '0.8')); +const churnRatioRaw = Number.parseFloat(getArgValue(args, '--churn-ratio', '0')); +const churnWarmSeconds = parseIntArg(getArgValue(args, '--churn-warm-seconds', '120'), 120); +const churnCoolSeconds = parseIntArg(getArgValue(args, '--churn-cool-seconds', '360'), 360); +const churnCohorts = Math.max(1, parseIntArg(getArgValue(args, '--churn-cohorts', '2'), 2)); +const requestProfilesFileArg = getArgValue(args, '--profiles', null); +const operationModeArg = getArgValue(args, '--operation-mode', 'auto'); +const keyspaceSize = Math.max(1, parseIntArg(getArgValue(args, '--keyspace-size', '1'), 1)); +const keyspaceModeArg = getArgValue(args, '--keyspace-mode', 'auto').trim().toLowerCase(); + +const profileLimit = parseIntArg(getArgValue(args, '--profile-limit', '0'), 0); +const heapPid = parseIntArg(getArgValue(args, '--heap-pid', ''), NaN); +const skipAnalyze = args.includes('--skip-analyze'); +const skipRouteProbe = args.includes('--skip-route-probe'); +const notePrefix = getArgValue(args, '--note-prefix', 'load-note'); + +const opWeightCreate = Number.parseFloat(getArgValue(args, '--op-weight-create', '0.2')); +const opWeightGetById = Number.parseFloat(getArgValue(args, '--op-weight-getbyid', '0.4')); +const opWeightUpdateById = Number.parseFloat(getArgValue(args, '--op-weight-updatebyid', '0.2')); +const opWeightListRecent = Number.parseFloat(getArgValue(args, '--op-weight-listrecent', '0.2')); +const failFastEnabled = !args.includes('--disable-fail-fast'); +const failFastWarmupSeconds = parseIntArg(getArgValue(args, '--fail-fast-warmup-seconds', '20'), 20); +const failFastMinTotal = parseIntArg(getArgValue(args, '--fail-fast-min-total', '1000'), 1000); +const failFastErrorRate = Math.max( + 0, + Math.min(1, Number.parseFloat(getArgValue(args, '--fail-fast-error-rate', '0.98'))), +); +const failFastConsecutiveNetworkErrors = parseIntArg( + getArgValue(args, '--fail-fast-consecutive-network-errors', '120'), + 120, +); +const publicAccessModeArg = getArgValue(args, '--public-access-mode', 'auto').trim().toLowerCase(); +const allowPublicAccessNonPerfSchema = args.includes('--allow-public-access-non-perf-schema'); +const publicRole = getArgValue(args, '--public-role', 'authenticated').trim(); +const publicReadRole = getArgValue(args, '--public-read-role', 'anonymous').trim(); +const prewarmEnabled = !args.includes('--disable-prewarm'); +const prewarmSampleSize = parseIntArg(getArgValue(args, '--prewarm-sample-size', '0'), 0); +const prewarmConcurrency = parseIntArg(getArgValue(args, '--prewarm-concurrency', '6'), 6); +const prewarmTimeoutMs = parseIntArg(getArgValue(args, '--prewarm-timeout-ms', '30000'), 30000); +const prewarmMaxFailures = parseIntArg(getArgValue(args, '--prewarm-max-failures', '0'), 0); + +const pgConfig = { + host: getArgValue(args, '--pg-host', process.env.PGHOST || 'localhost'), + port: Number.parseInt(getArgValue(args, '--pg-port', process.env.PGPORT || '5432'), 10), + database: getArgValue(args, '--pg-database', process.env.PGDATABASE || 'constructive'), + user: getArgValue(args, '--pg-user', process.env.PGUSER || 'postgres'), + password: getArgValue(args, '--pg-password', process.env.PGPASSWORD || 'password'), +}; + +const graphqlPayload = { + query: getArgValue(args, '--query', '{ __typename }'), +}; + +const VALID_KEYSPACE_MODES = new Set(['auto', 'schemata', 'none']); +if (!VALID_KEYSPACE_MODES.has(keyspaceModeArg)) { + throw new Error(`Invalid --keyspace-mode=${keyspaceModeArg}; expected auto|schemata|none`); +} +const VALID_PUBLIC_ACCESS_MODES = new Set(['auto', 'on', 'off']); +if (!VALID_PUBLIC_ACCESS_MODES.has(publicAccessModeArg)) { + throw new Error(`Invalid --public-access-mode=${publicAccessModeArg}; expected auto|on|off`); +} +if (!publicRole) { + throw new Error('--public-role cannot be empty'); +} +if (!Number.isFinite(prewarmSampleSize) || prewarmSampleSize < 0) { + throw new Error(`Invalid --prewarm-sample-size=${prewarmSampleSize}; expected non-negative integer`); +} +if (!Number.isFinite(prewarmConcurrency) || prewarmConcurrency <= 0) { + throw new Error(`Invalid --prewarm-concurrency=${prewarmConcurrency}; expected positive integer`); +} +if (!Number.isFinite(prewarmTimeoutMs) || prewarmTimeoutMs <= 0) { + throw new Error(`Invalid --prewarm-timeout-ms=${prewarmTimeoutMs}; expected positive integer`); +} +if (!Number.isFinite(prewarmMaxFailures) || prewarmMaxFailures < 0) { + throw new Error(`Invalid --prewarm-max-failures=${prewarmMaxFailures}; expected non-negative integer`); +} + +const resolveProfilesFile = async () => { + if (requestProfilesFileArg) { + return path.resolve(requestProfilesFileArg); + } + + const keyspacePath = path.join(dirs.dataDir, 'tokens.keyspace.json'); + try { + await fs.access(keyspacePath); + return keyspacePath; + } catch { + return path.join(dirs.dataDir, 'tokens.json'); + } +}; + +const ABSOLUTE_URL_RE = /^https?:\/\//i; + +const resolveProfileGraphqlUrl = (profile) => { + const profilePathRaw = profile?.graphqlUrl ?? '/graphql'; + if (ABSOLUTE_URL_RE.test(profilePathRaw)) { + return profilePathRaw; + } + + const profilePath = profilePathRaw.startsWith('/') ? profilePathRaw : `/${profilePathRaw}`; + return new URL(profilePath, baseUrl).toString(); +}; + +const quantile = (numbers, q) => { + if (numbers.length === 0) return null; + const sorted = [...numbers].sort((a, b) => a - b); + const index = Math.max(0, Math.min(sorted.length - 1, Math.floor((sorted.length - 1) * q))); + return sorted[index]; +}; + +const asCount = (value) => { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : 0; +}; + +const nonNegativeDelta = (startValue, endValue) => { + const delta = asCount(endValue) - asCount(startValue); + return delta >= 0 ? delta : 0; +}; + +const isMultiTenancyCacheStats = (value) => + value != null && Number.isFinite(Number(value.handlerCacheSize)); + +const cacheSizeDelta = (start, end) => { + const startBuildKeys = Array.isArray(start.buildKeys) ? start.buildKeys : []; + const endBuildKeys = Array.isArray(end.buildKeys) ? end.buildKeys : []; + const startBuildKeySet = new Set(startBuildKeys); + const endBuildKeySet = new Set(endBuildKeys); + const addedBuildKeys = endBuildKeys.filter((key) => !startBuildKeySet.has(key)); + const removedBuildKeys = startBuildKeys.filter((key) => !endBuildKeySet.has(key)); + + return { + kind: 'multiTenancyCache', + handlerCache: { + start: asCount(start.handlerCacheSize), + end: asCount(end.handlerCacheSize), + delta: asCount(end.handlerCacheSize) - asCount(start.handlerCacheSize), + max: asCount(end.handlerCacheMax ?? start.handlerCacheMax), + ttlMs: asCount(end.handlerCacheTtlMs ?? start.handlerCacheTtlMs), + }, + mappings: { + svcKeyStart: asCount(start.svcKeyMappings), + svcKeyEnd: asCount(end.svcKeyMappings), + svcKeyDelta: asCount(end.svcKeyMappings) - asCount(start.svcKeyMappings), + databaseIdStart: asCount(start.databaseIdMappings), + databaseIdEnd: asCount(end.databaseIdMappings), + databaseIdDelta: asCount(end.databaseIdMappings) - asCount(start.databaseIdMappings), + }, + inflightCreationsEnd: asCount(end.inflightCreations), + buildKeys: { + start: startBuildKeys.length, + end: endBuildKeys.length, + added: addedBuildKeys.length, + removed: removedBuildKeys.length, + addedSamples: addedBuildKeys.slice(0, 10), + removedSamples: removedBuildKeys.slice(0, 10), + }, + }; +}; + +const cacheActivityDelta = (start, end) => { + if (!start || !end) { + return null; + } + + if (isMultiTenancyCacheStats(start) || isMultiTenancyCacheStats(end)) { + return cacheSizeDelta(start, end); + } + + const lookupTotal = nonNegativeDelta(start.lookups?.total, end.lookups?.total); + const lookupHits = nonNegativeDelta(start.lookups?.hits, end.lookups?.hits); + const lookupMisses = nonNegativeDelta(start.lookups?.misses, end.lookups?.misses); + const lookupRecheckHits = nonNegativeDelta(start.lookups?.recheckHits, end.lookups?.recheckHits); + + return { + lookups: { + total: lookupTotal, + hits: lookupHits, + misses: lookupMisses, + recheckHits: lookupRecheckHits, + hitRate: lookupTotal > 0 ? Number((lookupHits / lookupTotal).toFixed(4)) : 0, + missRate: lookupTotal > 0 ? Number((lookupMisses / lookupTotal).toFixed(4)) : 0, + }, + builds: { + sets: nonNegativeDelta(start.builds?.sets, end.builds?.sets), + replacements: nonNegativeDelta(start.builds?.replacements, end.builds?.replacements), + coalescedWaits: nonNegativeDelta(start.builds?.coalescedWaits, end.builds?.coalescedWaits), + }, + evictions: { + total: nonNegativeDelta(start.evictions?.total, end.evictions?.total), + lru: nonNegativeDelta(start.evictions?.lru, end.evictions?.lru), + ttl: nonNegativeDelta(start.evictions?.ttl, end.evictions?.ttl), + manual: nonNegativeDelta(start.evictions?.manual, end.evictions?.manual), + }, + }; +}; + +const toMB = (bytes) => Number((bytes / 1024 / 1024).toFixed(2)); + +const toArray = (setLike) => [...setLike]; + +const summarizeCacheRedundancy = ( + snapshotJson, + { topFingerprints = 10, topKeys = 5, topKeyKinds = 5, topTenants = 5 } = {}, +) => { + const entries = Array.isArray(snapshotJson?.graphileCacheEntries) ? snapshotJson.graphileCacheEntries : []; + // `fingerprint` here comes from old-mode graphile-cache snapshot metadata. + // It is a perf comparison input, not part of the current buildKey runtime path. + const cacheSize = asCount(snapshotJson?.graphileCache?.size ?? entries.length); + const byFingerprint = new Map(); + const byKeyKind = new Map(); + const byTenant = new Map(); + + for (const entry of entries) { + const keyKind = entry?.keyKind ?? 'unknown'; + const tenant = entry?.databaseId ?? entry?.dbname ?? 'unknown'; + let keyKindRow = byKeyKind.get(keyKind); + if (!keyKindRow) { + keyKindRow = { + keyKind, + entries: 0, + duplicateEntries: 0, + redundantEntries: 0, + duplicateFingerprints: 0, + }; + byKeyKind.set(keyKind, keyKindRow); + } + keyKindRow.entries += 1; + + let tenantRow = byTenant.get(tenant); + if (!tenantRow) { + tenantRow = { + tenant, + entries: 0, + duplicateEntries: 0, + redundantEntries: 0, + duplicateFingerprints: 0, + keyKinds: new Set(), + }; + byTenant.set(tenant, tenantRow); + } + tenantRow.entries += 1; + tenantRow.keyKinds.add(keyKind); + + const fingerprint = entry?.fingerprint; + if (!fingerprint) continue; + + let row = byFingerprint.get(fingerprint); + if (!row) { + row = { + fingerprint, + entryCount: 0, + keyKinds: new Set(), + keys: [], + keyKindCounts: new Map(), + tenantCounts: new Map(), + dbnames: new Set(), + }; + byFingerprint.set(fingerprint, row); + } + + row.entryCount += 1; + row.keyKinds.add(keyKind); + row.keyKindCounts.set(keyKind, (row.keyKindCounts.get(keyKind) ?? 0) + 1); + row.tenantCounts.set(tenant, (row.tenantCounts.get(tenant) ?? 0) + 1); + if (entry?.dbname) { + row.dbnames.add(entry.dbname); + } + if (row.keys.length < topKeys && entry?.key) { + row.keys.push(entry.key); + } + } + + const duplicateBuckets = [...byFingerprint.values()] + .filter((row) => row.entryCount > 1) + .sort((a, b) => b.entryCount - a.entryCount || a.fingerprint.localeCompare(b.fingerprint)); + + const duplicateEntries = duplicateBuckets.reduce((acc, row) => acc + row.entryCount, 0); + const redundantEntries = duplicateBuckets.reduce((acc, row) => acc + Math.max(0, row.entryCount - 1), 0); + const crossKindDuplicateFingerprints = duplicateBuckets.reduce( + (acc, row) => acc + (row.keyKinds.size > 1 ? 1 : 0), + 0, + ); + const sameKindDuplicateFingerprints = duplicateBuckets.length - crossKindDuplicateFingerprints; + const crossKindDuplicateEntries = duplicateBuckets.reduce( + (acc, row) => acc + (row.keyKinds.size > 1 ? row.entryCount : 0), + 0, + ); + + for (const row of duplicateBuckets) { + for (const [keyKind, count] of row.keyKindCounts.entries()) { + const keyKindRow = byKeyKind.get(keyKind); + if (!keyKindRow) continue; + keyKindRow.duplicateEntries += count; + keyKindRow.redundantEntries += Math.max(0, count - 1); + keyKindRow.duplicateFingerprints += 1; + } + + for (const [tenant, count] of row.tenantCounts.entries()) { + const tenantRow = byTenant.get(tenant); + if (!tenantRow) continue; + tenantRow.duplicateEntries += count; + tenantRow.redundantEntries += Math.max(0, count - 1); + tenantRow.duplicateFingerprints += 1; + } + } + + const amplificationFactor = byFingerprint.size > 0 + ? Number((cacheSize / byFingerprint.size).toFixed(4)) + : 0; + const redundancyByKeyKind = [...byKeyKind.values()] + .map((row) => ({ + keyKind: row.keyKind, + entries: row.entries, + duplicateEntries: row.duplicateEntries, + redundantEntries: row.redundantEntries, + duplicateEntryRatio: row.entries > 0 ? Number((row.duplicateEntries / row.entries).toFixed(4)) : 0, + redundantEntryRatio: row.entries > 0 ? Number((row.redundantEntries / row.entries).toFixed(4)) : 0, + duplicateFingerprintRatio: duplicateBuckets.length > 0 + ? Number((row.duplicateFingerprints / duplicateBuckets.length).toFixed(4)) + : 0, + })) + .sort((a, b) => b.redundantEntries - a.redundantEntries || b.duplicateEntries - a.duplicateEntries) + .slice(0, topKeyKinds); + + const topDuplicateTenants = [...byTenant.values()] + .filter((row) => row.duplicateEntries > 0) + .map((row) => ({ + tenant: row.tenant, + entries: row.entries, + duplicateEntries: row.duplicateEntries, + redundantEntries: row.redundantEntries, + duplicateFingerprints: row.duplicateFingerprints, + keyKinds: toArray(row.keyKinds).sort(), + })) + .sort((a, b) => b.redundantEntries - a.redundantEntries || b.duplicateEntries - a.duplicateEntries) + .slice(0, topTenants); + + return { + observedEntries: entries.length, + cacheSize, + fingerprints: byFingerprint.size, + duplicateFingerprints: duplicateBuckets.length, + duplicateEntries, + redundantEntries, + duplicateEntryRatio: cacheSize > 0 ? Number((duplicateEntries / cacheSize).toFixed(4)) : 0, + redundantEntryRatio: cacheSize > 0 ? Number((redundantEntries / cacheSize).toFixed(4)) : 0, + crossKindDuplicateFingerprints, + sameKindDuplicateFingerprints, + crossKindDuplicateEntries, + crossKindDuplicateEntryRatio: cacheSize > 0 ? Number((crossKindDuplicateEntries / cacheSize).toFixed(4)) : 0, + amplificationFactor, + redundancyByKeyKind, + topDuplicateTenants, + topDuplicateFingerprints: duplicateBuckets.slice(0, topFingerprints).map((row) => ({ + fingerprint: row.fingerprint, + entryCount: row.entryCount, + redundantEntries: Math.max(0, row.entryCount - 1), + keyKinds: toArray(row.keyKinds).sort(), + keyKindCounts: Object.fromEntries([...row.keyKindCounts.entries()].sort((a, b) => b[1] - a[1])), + tenants: Object.fromEntries([...row.tenantCounts.entries()].sort((a, b) => b[1] - a[1])), + dbnames: toArray(row.dbnames).sort(), + keys: row.keys, + })), + }; +}; + +const getPerEntryHeapCostBytes = ({ startSnapshot, endSnapshot }) => { + const heapDeltaBytes = asCount(endSnapshot?.memory?.heapUsedBytes) - asCount(startSnapshot?.memory?.heapUsedBytes); + const cacheDelta = asCount(endSnapshot?.graphileCache?.size) - asCount(startSnapshot?.graphileCache?.size); + if (heapDeltaBytes <= 0 || cacheDelta <= 0) { + return null; + } + return Math.round(heapDeltaBytes / cacheDelta); +}; + +const analyzeCacheRedundancyRisk = ({ baselineSnapshot, afterSnapshot, idleSnapshot }) => { + const baseline = summarizeCacheRedundancy(baselineSnapshot); + const after = summarizeCacheRedundancy(afterSnapshot); + const idle = summarizeCacheRedundancy(idleSnapshot); + + const perEntryHeapFromLoadBytes = getPerEntryHeapCostBytes({ + startSnapshot: baselineSnapshot, + endSnapshot: afterSnapshot, + }); + + const upperBoundPerEntryHeapBytes = (() => { + const cacheSize = asCount(afterSnapshot?.graphileCache?.size); + const heapUsed = asCount(afterSnapshot?.memory?.heapUsedBytes); + if (cacheSize <= 0 || heapUsed <= 0) { + return null; + } + return Math.round(heapUsed / cacheSize); + })(); + + const estimates = { + perEntryHeapCostFromLoadMb: + perEntryHeapFromLoadBytes == null ? null : toMB(perEntryHeapFromLoadBytes), + perEntryHeapCostUpperBoundMb: + upperBoundPerEntryHeapBytes == null ? null : toMB(upperBoundPerEntryHeapBytes), + redundantHeapCostFromLoadMb: + perEntryHeapFromLoadBytes == null ? null : toMB(perEntryHeapFromLoadBytes * after.redundantEntries), + redundantHeapCostUpperBoundMb: + upperBoundPerEntryHeapBytes == null ? null : toMB(upperBoundPerEntryHeapBytes * after.redundantEntries), + }; + + const riskSignals = []; + if (after.redundantEntryRatio >= 0.2) { + riskSignals.push('after-load redundantEntryRatio >= 20%'); + } + if (idle.redundantEntryRatio >= 0.2) { + riskSignals.push('post-idle redundantEntryRatio >= 20%'); + } + if (after.crossKindDuplicateFingerprints > 0) { + riskSignals.push('cross-keyKind duplicate fingerprints observed'); + } + if (after.amplificationFactor >= 1.3) { + riskSignals.push('cache amplification factor >= 1.3 (same runtime cached under many keys)'); + } + if (after.sameKindDuplicateFingerprints > 0 && after.redundantEntryRatio >= 0.1) { + riskSignals.push('same-keyKind duplicates observed with non-trivial redundant ratio'); + } + if ((estimates.redundantHeapCostFromLoadMb ?? 0) >= 300) { + riskSignals.push('estimated redundant heap cost from load delta >= 300MB'); + } + + return { + baseline, + after, + idle, + estimates, + riskSignals, + }; +}; + +const extractCacheActivity = (debugPayload) => + debugPayload?.json?.graphileCacheActivity ?? debugPayload?.json?.multiTenancyCache ?? null; + +const clamp01 = (value) => { + if (!Number.isFinite(value)) return 0; + return Math.max(0, Math.min(1, value)); +}; + +const isLikelyNetworkError = (errorMessage) => { + if (!errorMessage) return false; + const text = String(errorMessage).toLowerCase(); + return ( + text.includes('fetch failed') || + text.includes('econnrefused') || + text.includes('connection terminated') || + text.includes('connection reset') || + text.includes('socket hang up') || + text.includes('networkerror') || + text.includes('network error') || + text.includes('timeout') + ); +}; + +const isBusinessProfile = (profile) => + !!( + profile?.table?.typeName && + profile?.table?.queryField && + profile?.table?.createMutation && + profile?.table?.updateMutation + ); + +const isPublicBusinessProfile = (profile) => + isBusinessProfile(profile) && + (profile?.routingMode === 'public' || (!profile?.headers?.['X-Schemata'] && !!profile?.headers?.Host)); + +const normalizeWeights = () => { + const weights = { + create: clamp01(opWeightCreate), + getById: clamp01(opWeightGetById), + updateById: clamp01(opWeightUpdateById), + listRecent: clamp01(opWeightListRecent), + }; + + const sum = Object.values(weights).reduce((acc, value) => acc + value, 0); + if (sum <= 0) { + return { + create: 0.25, + getById: 0.25, + updateById: 0.25, + listRecent: 0.25, + }; + } + + return { + create: weights.create / sum, + getById: weights.getById / sum, + updateById: weights.updateById / sum, + listRecent: weights.listRecent / sum, + }; +}; + +const pickOperation = (weights) => { + const dice = Math.random(); + const edges = [ + ['create', weights.create], + ['getById', weights.getById], + ['updateById', weights.updateById], + ['listRecent', weights.listRecent], + ]; + + let cursor = 0; + for (const [name, weight] of edges) { + cursor += weight; + if (dice <= cursor) return name; + } + return 'listRecent'; +}; + +const resolveBusinessKeyspaceMode = (profiles) => { + if (keyspaceModeArg !== 'auto') { + return keyspaceModeArg; + } + + if (keyspaceSize <= 1) { + return 'none'; + } + + const hasSchemataHeader = profiles.some((profile) => { + const value = profile?.headers?.['X-Schemata']; + return typeof value === 'string' && value.trim().length > 0; + }); + + return hasSchemataHeader ? 'schemata' : 'none'; +}; + +const expandBusinessProfilesForKeyspace = ({ profiles, keyspaceMode }) => { + if (keyspaceMode !== 'schemata' || keyspaceSize <= 1) { + return profiles; + } + + const expanded = []; + + for (const profile of profiles) { + const baseSchema = profile.headers?.['X-Schemata'] || profile.table?.physicalSchema; + if (!baseSchema) { + expanded.push(profile); + continue; + } + + const schemaPool = Array.isArray(profile.table?.availableSchemas) + ? profile.table.availableSchemas.filter(Boolean) + : []; + const extras = [...new Set(schemaPool.filter((schema) => schema !== baseSchema))]; + + for (let i = 0; i < keyspaceSize; i += 1) { + const keyspaceIndex = i + 1; + const headers = { ...(profile.headers || {}) }; + + if (keyspaceIndex === 1 || extras.length === 0) { + headers['X-Schemata'] = baseSchema; + } else { + const extra = extras[(i - 1) % extras.length]; + headers['X-Schemata'] = `${baseSchema},${extra}`; + } + + expanded.push({ + ...profile, + key: `${profile.key}|k${keyspaceIndex}`, + routeKey: `schemata:${headers['X-Database-Id']}:${headers['X-Schemata']}`, + headers, + }); + } + } + + return expanded.length > 0 ? expanded : profiles; +}; + +const buildBusinessRequest = ({ profile, operation, rowId }) => { + const table = profile.table; + const noteValue = `${notePrefix}-${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`; + + switch (operation) { + case 'create': + return { + operation: 'create', + payload: { + query: `mutation($input: ${table.createInputType}!){${table.createMutation}(input:$input){${table.nodeField}{id note}}}`, + variables: { + input: { + [table.nodeField]: { + note: noteValue, + }, + }, + }, + }, + }; + case 'getById': + return { + operation: 'getById', + payload: { + query: `query($id:UUID!){${table.queryField}(where:{id:{equalTo:$id}},first:1){nodes{id note}}}`, + variables: { + id: rowId, + }, + }, + }; + case 'updateById': + return { + operation: 'updateById', + payload: { + query: `mutation($input: ${table.updateInputType}!){${table.updateMutation}(input:$input){${table.nodeField}{id note}}}`, + variables: { + input: { + id: rowId, + [table.patchField]: { + note: noteValue, + }, + }, + }, + }, + }; + default: + return { + operation: 'listRecent', + payload: { + query: `query{${table.queryField}(first:10,orderBy:[PRIMARY_KEY_DESC]){nodes{id note}}}`, + }, + }; + } +}; + +const extractCreatedRowId = ({ profile, json, operation }) => { + if (operation === 'create') { + return json?.data?.[profile.table.createMutation]?.[profile.table.nodeField]?.id ?? null; + } + if (operation === 'updateById') { + return json?.data?.[profile.table.updateMutation]?.[profile.table.nodeField]?.id ?? null; + } + if (operation === 'getById') { + return json?.data?.[profile.table.queryField]?.nodes?.[0]?.id ?? null; + } + if (operation === 'listRecent') { + return json?.data?.[profile.table.queryField]?.nodes?.[0]?.id ?? null; + } + return null; +}; + +const captureDebug = async ({ suffix }) => { + const tierDir = path.join(dirs.dataDir, 'snapshots', tier); + await fs.mkdir(tierDir, { recursive: true }); + + const [memory, db] = await Promise.all([ + getJson({ url: `${baseUrl}/debug/memory`, timeoutMs: 15000 }), + getJson({ url: `${baseUrl}/debug/db`, timeoutMs: 15000 }), + ]); + + const memoryPath = path.join(tierDir, `memory-${suffix}.json`); + const dbPath = path.join(tierDir, `db-${suffix}.json`); + + await writeJson(memoryPath, memory); + await writeJson(dbPath, db); + + return { memoryPath, dbPath, memory, db }; +}; + +const loadProfiles = async (filePath) => { + const raw = await fs.readFile(filePath, 'utf8'); + const parsed = JSON.parse(raw); + const profiles = Array.isArray(parsed) ? parsed : parsed?.profiles; + if (!Array.isArray(profiles) || profiles.length === 0) { + throw new Error(`No request profiles found in ${filePath}`); + } + + const mode = + operationModeArg === 'auto' + ? profiles.some((profile) => isBusinessProfile(profile)) + ? 'business' + : 'legacy' + : operationModeArg; + + let resolved = profiles; + let resolvedKeyspaceMode = 'none'; + if (mode === 'business') { + resolvedKeyspaceMode = resolveBusinessKeyspaceMode(profiles); + resolved = expandBusinessProfilesForKeyspace({ + profiles, + keyspaceMode: resolvedKeyspaceMode, + }); + } + + if (profileLimit > 0) { + resolved = resolved.slice(0, profileLimit); + } + + return { profiles: resolved, mode, sourceCount: profiles.length, keyspaceMode: resolvedKeyspaceMode }; +}; + +const validateScaleGate = async ({ profiles }) => { + const distinctTenantKeys = new Set( + profiles.map((profile) => profile.tenantKey || profile.key).filter(Boolean), + ).size; + + const profileMsg = `token tenant coverage=${distinctTenantKeys}, required>=${requireTenants}`; + if (distinctTenantKeys < requireTenants) { + if (enforceTenantScale) { + throw new Error(`Scale gate failed: ${profileMsg}`); + } + console.warn(`[phase2] ${profileMsg}; continuing due to --allow-underprovisioned`); + } + + const preflightPath = path.join(runDir, 'reports', 'preflight.json'); + try { + const preflightRaw = await fs.readFile(preflightPath, 'utf8'); + const preflight = JSON.parse(preflightRaw); + const phase1Ready = !!preflight?.readiness?.phase1Ready; + const tenantReady = !!preflight?.readiness?.tenantReadyForPhase2; + + if (!phase1Ready || !tenantReady) { + const msg = `preflight readiness not satisfied (phase1Ready=${phase1Ready}, tenantReadyForPhase2=${tenantReady})`; + if (enforceTenantScale) { + throw new Error(`Scale gate failed: ${msg}`); + } + console.warn(`[phase2] ${msg}; continuing due to --allow-underprovisioned`); + } + } catch (error) { + const msg = `preflight report missing or unreadable at ${preflightPath}`; + if (enforceTenantScale) { + throw new Error(`Scale gate failed: ${msg}`); + } + console.warn(`[phase2] ${msg}; continuing due to --allow-underprovisioned`); + if (error instanceof Error) { + console.warn(`[phase2] detail: ${error.message}`); + } + } +}; + +const runRouteProbe = async ({ profiles }) => { + if (skipRouteProbe) { + return { attempted: false, reason: 'skip route probe enabled' }; + } + + if (!Array.isArray(profiles) || profiles.length === 0) { + throw new Error('[phase2] route probe cannot run without profiles'); + } + + const profile = profiles[0]; + const payload = isBusinessProfile(profile) + ? { query: `query{${profile.table.queryField}(first:1){nodes{id}}}` } + : { query: '{ __typename }' }; + const result = await postJson({ + url: resolveProfileGraphqlUrl(profile), + headers: profile.headers ?? {}, + payload, + timeoutMs: 15000, + }); + + const hasGraphQLErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0; + const ok = result.ok && !hasGraphQLErrors && result.json?.data != null; + + const probe = { + attempted: true, + ok, + profileKey: profile.key ?? null, + status: result.status, + elapsedMs: result.elapsedMs, + error: result.error ?? null, + firstError: hasGraphQLErrors ? result.json.errors[0]?.message ?? 'unknown GraphQL error' : null, + }; + + if (!ok) { + const msg = probe.error || probe.firstError || 'unexpected GraphQL response'; + throw new Error( + `[phase2] route probe failed for profile=${probe.profileKey ?? 'unknown'} status=${probe.status ?? 0} msg=${msg}`, + ); + } + + return probe; +}; + +const pickPrewarmProfiles = (profiles) => { + if (prewarmSampleSize <= 0 || prewarmSampleSize >= profiles.length) { + return profiles; + } + + const selected = []; + const selectedKeys = new Set(); + const selectedTenantKeys = new Set(); + + for (const profile of profiles) { + const profileKey = profile?.key ?? null; + if (!profileKey || selectedKeys.has(profileKey)) continue; + const tenantKey = profile?.tenantKey ?? profileKey; + if (selectedTenantKeys.has(tenantKey)) continue; + + selected.push(profile); + selectedKeys.add(profileKey); + selectedTenantKeys.add(tenantKey); + if (selected.length >= prewarmSampleSize) { + return selected; + } + } + + for (const profile of profiles) { + const profileKey = profile?.key ?? null; + if (!profileKey || selectedKeys.has(profileKey)) continue; + selected.push(profile); + selectedKeys.add(profileKey); + if (selected.length >= prewarmSampleSize) { + break; + } + } + + return selected; +}; + +const runPrewarm = async ({ profiles }) => { + if (!prewarmEnabled) { + return { + attempted: false, + enabled: false, + reason: 'prewarm disabled', + requestedSampleSize: prewarmSampleSize, + }; + } + + if (!Array.isArray(profiles) || profiles.length === 0) { + return { + attempted: false, + enabled: true, + reason: 'no profiles', + requestedSampleSize: prewarmSampleSize, + }; + } + + const selectedProfiles = pickPrewarmProfiles(profiles); + const failures = []; + let ok = 0; + let cursor = 0; + const workerCount = Math.max(1, Math.min(prewarmConcurrency, selectedProfiles.length)); + const startedAt = Date.now(); + + const worker = async () => { + while (true) { + const index = cursor; + cursor += 1; + if (index >= selectedProfiles.length) { + return; + } + + const profile = selectedProfiles[index]; + const result = await postJson({ + url: resolveProfileGraphqlUrl(profile), + headers: profile.headers ?? {}, + payload: { query: '{ __typename }' }, + timeoutMs: prewarmTimeoutMs, + }); + + const hasGraphQLErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0; + const success = result.ok && !hasGraphQLErrors && result.json?.data?.__typename === 'Query'; + if (success) { + ok += 1; + continue; + } + + failures.push({ + profileKey: profile?.key ?? null, + tenantKey: profile?.tenantKey ?? null, + status: result.status, + elapsedMs: result.elapsedMs, + error: + result.error ?? + (hasGraphQLErrors ? result.json.errors[0]?.message ?? 'unknown GraphQL error' : 'unexpected GraphQL response'), + }); + } + }; + + await Promise.all(Array.from({ length: workerCount }, () => worker())); + + const summary = { + attempted: true, + enabled: true, + startedAt: new Date(startedAt).toISOString(), + endedAt: new Date().toISOString(), + requestedSampleSize: prewarmSampleSize, + targetCount: selectedProfiles.length, + ok, + failed: failures.length, + concurrency: workerCount, + timeoutMs: prewarmTimeoutMs, + maxFailures: prewarmMaxFailures, + failureSamples: failures.slice(0, 10), + }; + + if (summary.failed > prewarmMaxFailures) { + throw new Error( + `[phase2] prewarm failed count=${summary.failed}, allowed=${prewarmMaxFailures}; first=${summary.failureSamples[0]?.error ?? 'unknown error'}`, + ); + } + + return summary; +}; + +const chooseProfile = (profiles) => { + if (!Array.isArray(profiles) || profiles.length === 0) { + throw new Error('No active profiles available for selection'); + } + if (profiles.length === 1) return profiles[0]; + + const hotCount = Math.max(1, Math.floor(profiles.length * 0.2)); + const hot = profiles.slice(0, hotCount); + const cold = profiles.slice(hotCount); + + const hotPick = Math.random() < hotRatio || cold.length === 0; + const target = hotPick ? hot : cold; + return target[Math.floor(Math.random() * target.length)]; +}; + +const buildTrafficPlan = (profiles) => { + const churnRatio = clamp01(churnRatioRaw); + const churnEnabled = churnRatio > 0 && churnCoolSeconds > 0; + const maxChurnCount = Math.max(0, profiles.length - 1); + const churnCount = churnEnabled ? Math.min(Math.floor(profiles.length * churnRatio), maxChurnCount) : 0; + const alwaysOnProfiles = profiles.slice(0, profiles.length - churnCount); + const churnProfiles = profiles.slice(profiles.length - churnCount); + const cooldownWindowsByKey = new Map(); + + for (let i = 0; i < churnProfiles.length; i += 1) { + const profile = churnProfiles[i]; + const key = profile.key ?? `profile-${i}`; + const cohort = i % churnCohorts; + const cohortOffsetSeconds = Math.floor((cohort * churnCoolSeconds) / churnCohorts); + const coolStartSeconds = Math.max(0, churnWarmSeconds + cohortOffsetSeconds); + const coolEndSeconds = coolStartSeconds + churnCoolSeconds; + cooldownWindowsByKey.set(key, { cohort, coolStartSeconds, coolEndSeconds }); + } + + let lastElapsedSecond = -1; + let cachedActiveProfiles = profiles; + + const getActiveProfiles = (elapsedMs) => { + if (!churnEnabled || churnCount === 0) { + return profiles; + } + + const elapsedSecond = Math.max(0, Math.floor(elapsedMs / 1000)); + if (elapsedSecond === lastElapsedSecond) { + return cachedActiveProfiles; + } + lastElapsedSecond = elapsedSecond; + + const activeFromChurn = churnProfiles.filter((profile) => { + const window = cooldownWindowsByKey.get(profile.key ?? ''); + if (!window) return true; + return elapsedSecond < window.coolStartSeconds || elapsedSecond >= window.coolEndSeconds; + }); + + const active = [...alwaysOnProfiles, ...activeFromChurn]; + cachedActiveProfiles = active.length > 0 ? active : alwaysOnProfiles.length > 0 ? alwaysOnProfiles : profiles; + return cachedActiveProfiles; + }; + + return { + getActiveProfiles, + summary: { + enabled: churnEnabled && churnCount > 0, + ratio: churnRatio, + warmSeconds: churnWarmSeconds, + coolSeconds: churnCoolSeconds, + cohorts: churnCohorts, + churnProfileCount: churnCount, + alwaysOnProfileCount: alwaysOnProfiles.length, + windows: Object.fromEntries(cooldownWindowsByKey.entries()), + }, + }; +}; + +const runLoad = async ({ profiles, mode }) => { + const startedAt = Date.now(); + const until = startedAt + durationSeconds * 1000; + const trafficPlan = buildTrafficPlan(profiles); + const operationWeights = normalizeWeights(); + const rowState = new Map(); + let abortReason = null; + let consecutiveNetworkErrors = 0; + + const latencies = []; + const profileStats = new Map(); + const operationStats = new Map(); + let total = 0; + let ok = 0; + let failed = 0; + + const recordOperation = (operation, success) => { + if (!operationStats.has(operation)) { + operationStats.set(operation, { total: 0, ok: 0, failed: 0 }); + } + const row = operationStats.get(operation); + row.total += 1; + row.ok += success ? 1 : 0; + row.failed += success ? 0 : 1; + }; + + const record = (profileKey, success, elapsedMs, status, error, operation) => { + total += 1; + if (success) { + ok += 1; + } else { + failed += 1; + } + + if (latencies.length < 20000) { + latencies.push(elapsedMs); + } + + if (!profileStats.has(profileKey)) { + profileStats.set(profileKey, { + total: 0, + ok: 0, + failed: 0, + maxMs: 0, + minMs: Number.POSITIVE_INFINITY, + statuses: {}, + lastError: null, + }); + } + + const row = profileStats.get(profileKey); + row.total += 1; + row.ok += success ? 1 : 0; + row.failed += success ? 0 : 1; + row.maxMs = Math.max(row.maxMs, elapsedMs); + row.minMs = Math.min(row.minMs, elapsedMs); + row.statuses[String(status)] = (row.statuses[String(status)] ?? 0) + 1; + if (error) { + row.lastError = error; + } + if (operation) { + row.lastOperation = operation; + } + + if (operation) { + recordOperation(operation, success); + } + }; + + const getRowBucket = (profileKey, seedRowId = null) => { + if (!rowState.has(profileKey)) { + rowState.set(profileKey, { + rowIds: seedRowId ? [seedRowId] : [], + }); + } + return rowState.get(profileKey); + }; + + const worker = async () => { + while (Date.now() < until && !abortReason) { + const elapsedMs = Date.now() - startedAt; + const activeProfiles = trafficPlan.getActiveProfiles(elapsedMs); + const profile = chooseProfile(activeProfiles); + const profileKey = profile.key ?? 'unknown'; + + let operation = 'legacy'; + let payload = graphqlPayload; + + if (mode === 'business') { + const bucket = getRowBucket(profileKey, profile.seed?.rowId ?? null); + operation = pickOperation(operationWeights); + if ((operation === 'getById' || operation === 'updateById') && bucket.rowIds.length === 0) { + operation = 'create'; + } + + const selectedRowId = + bucket.rowIds.length > 0 + ? bucket.rowIds[Math.floor(Math.random() * bucket.rowIds.length)] + : null; + + const request = buildBusinessRequest({ + profile, + operation, + rowId: selectedRowId, + }); + operation = request.operation; + payload = request.payload; + } + + const result = await postJson({ + url: resolveProfileGraphqlUrl(profile), + headers: profile.headers ?? {}, + payload, + timeoutMs: 20000, + }); + + const hasGraphQLErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0; + const success = result.ok && !hasGraphQLErrors && result.json?.data != null; + const errMsg = result.error + ? result.error + : result.json?.errors?.[0]?.message ?? (!success ? 'unexpected GraphQL response' : null); + + if (mode === 'business' && success) { + const bucket = getRowBucket(profileKey, profile.seed?.rowId ?? null); + const rowId = extractCreatedRowId({ profile, json: result.json, operation }); + if (rowId && !bucket.rowIds.includes(rowId)) { + bucket.rowIds.push(rowId); + if (bucket.rowIds.length > 200) { + bucket.rowIds = bucket.rowIds.slice(bucket.rowIds.length - 200); + } + } + } + + record(profileKey, success, result.elapsedMs, result.status, errMsg, operation); + + if (!failFastEnabled || abortReason) { + continue; + } + + if (success) { + consecutiveNetworkErrors = 0; + } else if (isLikelyNetworkError(errMsg)) { + consecutiveNetworkErrors += 1; + } else { + consecutiveNetworkErrors = 0; + } + + if (consecutiveNetworkErrors >= failFastConsecutiveNetworkErrors) { + abortReason = `consecutive network errors=${consecutiveNetworkErrors} (threshold=${failFastConsecutiveNetworkErrors}), lastError=${errMsg ?? 'n/a'}`; + continue; + } + + const elapsedSeconds = (Date.now() - startedAt) / 1000; + if (elapsedSeconds < failFastWarmupSeconds || total < failFastMinTotal) { + continue; + } + + const currentFailRate = failed / Math.max(1, total); + if (currentFailRate >= failFastErrorRate) { + abortReason = `error rate=${(currentFailRate * 100).toFixed(2)}% (threshold=${(failFastErrorRate * 100).toFixed(2)}%) after total=${total}, failed=${failed}`; + } + } + }; + + await Promise.all(Array.from({ length: workers }, () => worker())); + + if (abortReason) { + throw new Error(`[phase2] fail-fast triggered: ${abortReason}`); + } + + const elapsedMs = Date.now() - startedAt; + const profileBreakdown = Object.fromEntries(profileStats.entries()); + + for (const key of Object.keys(profileBreakdown)) { + const stats = profileBreakdown[key]; + if (!Number.isFinite(stats.minMs)) stats.minMs = 0; + } + + return { + startedAt: new Date(startedAt).toISOString(), + endedAt: new Date().toISOString(), + elapsedMs, + total, + ok, + failed, + requestsPerSecond: elapsedMs > 0 ? Number((total / (elapsedMs / 1000)).toFixed(2)) : 0, + latencyMs: { + min: latencies.length > 0 ? Math.min(...latencies) : null, + p50: quantile(latencies, 0.5), + p95: quantile(latencies, 0.95), + p99: quantile(latencies, 0.99), + max: latencies.length > 0 ? Math.max(...latencies) : null, + sampleCount: latencies.length, + }, + mode, + operationWeights: mode === 'business' ? operationWeights : null, + operationBreakdown: Object.fromEntries(operationStats.entries()), + trafficPlan: trafficPlan.summary, + profileBreakdown, + }; +}; + +const preparePublicAccess = async ({ profiles }) => { + if (!Array.isArray(profiles) || profiles.length === 0) { + return { attempted: false, enabled: false, mode: publicAccessModeArg, reason: 'no profiles' }; + } + + const hasPublicBusinessProfiles = profiles.some((profile) => isPublicBusinessProfile(profile)); + const enabled = + publicAccessModeArg === 'on' || + (publicAccessModeArg === 'auto' && hasPublicBusinessProfiles); + + if (!enabled) { + return { + attempted: false, + enabled: false, + mode: publicAccessModeArg, + hasPublicBusinessProfiles, + reason: publicAccessModeArg === 'off' ? 'public access mode disabled' : 'no public business profiles', + }; + } + + const targets = buildTargetsFromProfiles(profiles); + if (targets.length === 0) { + return { + attempted: false, + enabled: true, + mode: publicAccessModeArg, + hasPublicBusinessProfiles, + reason: 'no table targets in profiles', + }; + } + + const unsafeTargets = getUnsafeTargets(targets); + if (unsafeTargets.length > 0 && !allowPublicAccessNonPerfSchema) { + throw new Error( + `[phase2] refusing to prepare non-perf schemas: ${unsafeTargets + .map((target) => `${target.schemaName}.${target.tableName}`) + .join(', ')}`, + ); + } + + const result = await ensurePublicAccessForTargets({ + targets, + pgConfig, + dryRun: false, + publicRole, + publicReadRole, + }); + + if (result.failures.length > 0) { + throw new Error( + `[phase2] public access preparation failed count=${result.failures.length}; first=${result.failures[0]?.error ?? 'unknown error'}`, + ); + } + + return { + attempted: true, + enabled: true, + mode: publicAccessModeArg, + hasPublicBusinessProfiles, + targetCount: targets.length, + preparedCount: result.prepared.length, + failureCount: result.failures.length, + publicRole, + publicReadRole: publicReadRole || null, + }; +}; + +const tryCaptureHeap = async () => { + if (!Number.isFinite(heapPid)) { + return { attempted: false, reason: 'heap pid not provided' }; + } + + const scriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'capture-heap-snapshot.mjs'); + + return await new Promise((resolve) => { + const child = spawn(process.execPath, [scriptPath, '--pid', String(heapPid), '--dir', dirs.heapDir, '--timeout-ms', '60000'], { + stdio: ['ignore', 'pipe', 'pipe'], + }); + + let stdout = ''; + let stderr = ''; + child.stdout.on('data', (chunk) => { + stdout += String(chunk); + }); + child.stderr.on('data', (chunk) => { + stderr += String(chunk); + }); + + child.on('close', (code) => { + resolve({ + attempted: true, + ok: code === 0, + code, + output: stdout.trim(), + error: stderr.trim() || null, + }); + }); + }); +}; + +const analyzeSampler = async ({ windowStart, windowEnd }) => { + if (skipAnalyze) { + return { attempted: false, reason: 'skip analyze enabled' }; + } + + const scriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'analyze-debug-logs.mjs'); + const childArgs = [scriptPath, '--dir', dirs.samplerDir, '--json', '--start', windowStart, '--end', windowEnd]; + return await new Promise((resolve) => { + const child = spawn(process.execPath, childArgs, { + stdio: ['ignore', 'pipe', 'pipe'], + }); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (chunk) => { + stdout += String(chunk); + }); + child.stderr.on('data', (chunk) => { + stderr += String(chunk); + }); + + child.on('close', async (code) => { + if (code === 0) { + try { + const parsed = JSON.parse(stdout); + const reportPath = path.join(dirs.reportsDir, `analyze-debug-logs-${tier}.json`); + await writeJson(reportPath, parsed); + resolve({ attempted: true, ok: true, reportPath, stderr: stderr.trim() || null }); + return; + } catch { + resolve({ attempted: true, ok: false, error: 'analyze output is not valid JSON', stderr: stderr.trim() || null }); + return; + } + } + + resolve({ attempted: true, ok: false, code, error: stderr.trim() || 'analyze script failed' }); + }); + }); +}; + +const main = async () => { + const startedAt = new Date().toISOString(); + const requestProfilesFile = await resolveProfilesFile(); + const loadedProfiles = await loadProfiles(requestProfilesFile); + const profiles = loadedProfiles.profiles; + const executionMode = loadedProfiles.mode; + const executionKeyspaceMode = loadedProfiles.keyspaceMode; + const publicAccessPreparation = await preparePublicAccess({ profiles }); + await validateScaleGate({ profiles }); + const routeProbe = await runRouteProbe({ profiles }); + const prewarm = await runPrewarm({ profiles }); + + const baseline = await captureDebug({ suffix: 'baseline' }); + const loadSummary = await runLoad({ profiles, mode: executionMode }); + const after = await captureDebug({ suffix: 'after' }); + + await sleep(idleSeconds * 1000); + const idle = await captureDebug({ suffix: 'idle' }); + + const cacheActivity = { + loadWindow: cacheActivityDelta( + extractCacheActivity(baseline.memory), + extractCacheActivity(after.memory), + ), + cooldownWindow: cacheActivityDelta( + extractCacheActivity(after.memory), + extractCacheActivity(idle.memory), + ), + totalWindow: cacheActivityDelta( + extractCacheActivity(baseline.memory), + extractCacheActivity(idle.memory), + ), + }; + const cacheRedundancy = analyzeCacheRedundancyRisk({ + baselineSnapshot: baseline.memory?.json ?? null, + afterSnapshot: after.memory?.json ?? null, + idleSnapshot: idle.memory?.json ?? null, + }); + + const analyzeWindowStart = baseline.memory?.json?.timestamp ?? startedAt; + const analyzeWindowEnd = idle.memory?.json?.timestamp ?? new Date().toISOString(); + const [heapCapture, analyzeResult] = await Promise.all([ + tryCaptureHeap(), + analyzeSampler({ windowStart: analyzeWindowStart, windowEnd: analyzeWindowEnd }), + ]); + + const result = { + startedAt, + endedAt: new Date().toISOString(), + runDir, + tier, + baseUrl, + workers, + durationSeconds, + idleSeconds, + hotRatio, + keyspaceSize, + keyspaceMode: executionKeyspaceMode, + keyspaceModeRequested: keyspaceModeArg, + operationMode: executionMode, + publicAccessPreparation, + routeProbe, + prewarmConfig: { + enabled: prewarmEnabled, + sampleSize: prewarmSampleSize, + concurrency: prewarmConcurrency, + timeoutMs: prewarmTimeoutMs, + maxFailures: prewarmMaxFailures, + }, + prewarm, + profilesFile: requestProfilesFile, + sourceProfileCount: loadedProfiles.sourceCount, + profileCount: profiles.length, + snapshots: { + baseline: { + memoryPath: baseline.memoryPath, + dbPath: baseline.dbPath, + memoryOk: baseline.memory.ok, + dbOk: baseline.db.ok, + }, + after: { + memoryPath: after.memoryPath, + dbPath: after.dbPath, + memoryOk: after.memory.ok, + dbOk: after.db.ok, + }, + idle: { + memoryPath: idle.memoryPath, + dbPath: idle.dbPath, + memoryOk: idle.memory.ok, + dbOk: idle.db.ok, + }, + }, + load: loadSummary, + cacheActivity, + cacheRedundancy, + heapCapture, + analyzeResult, + }; + + const outPath = path.join(dirs.dataDir, `load-${tier}.json`); + await writeJson(outPath, result); + console.log(JSON.stringify({ outPath, profileCount: profiles.length, totalRequests: loadSummary.total, failed: loadSummary.failed }, null, 2)); +}; + +main().catch((error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exit(1); +}); diff --git a/graphql/server/perf/src/legacy/public-test-access-lib.ts b/graphql/server/perf/src/legacy/public-test-access-lib.ts new file mode 100644 index 0000000000..5eb4263c54 --- /dev/null +++ b/graphql/server/perf/src/legacy/public-test-access-lib.ts @@ -0,0 +1,178 @@ +// @ts-nocheck +import { Pool } from 'pg'; + +const quoteIdent = (value) => `"${String(value).replace(/"/g, '""')}"`; +const quoteIdentOrNull = (value) => { + const text = String(value ?? '').trim(); + return text.length > 0 ? quoteIdent(text) : null; +}; + +const POLICY_NAMES = { + select: 'perf_load_public_select', + insert: 'perf_load_public_insert', + update: 'perf_load_public_update', +}; + +const policySuffixForRole = (role) => { + const suffix = String(role ?? '') + .trim() + .toLowerCase() + .replace(/[^a-z0-9_]+/g, '_') + .replace(/^_+|_+$/g, ''); + return suffix || 'role'; +}; + +const policyNamesForRole = (role) => { + const suffix = policySuffixForRole(role); + return { + select: `${POLICY_NAMES.select}_${suffix}`, + insert: `${POLICY_NAMES.insert}_${suffix}`, + update: `${POLICY_NAMES.update}_${suffix}`, + }; +}; + +export const extractProfiles = (payload) => { + if (Array.isArray(payload)) return payload; + if (Array.isArray(payload?.profiles)) return payload.profiles; + return []; +}; + +export const buildTargetsFromProfiles = (profiles) => { + const dedupe = new Map(); + + for (const profile of profiles) { + const schemaName = profile?.table?.physicalSchema; + const tableName = profile?.table?.tableName; + if (!schemaName || !tableName) continue; + dedupe.set(`${schemaName}.${tableName}`, { + schemaName, + tableName, + databaseId: profile?.table?.databaseId ?? null, + profileKey: profile?.key ?? null, + }); + } + + return [...dedupe.values()].sort((a, b) => + `${a.schemaName}.${a.tableName}`.localeCompare(`${b.schemaName}.${b.tableName}`), + ); +}; + +export const getUnsafeTargets = (targets) => + targets.filter((target) => !target.schemaName.startsWith('perf-')); + +export const ensurePublicAccessForTargets = async ({ + targets, + pgConfig, + dryRun = false, + publicRole = 'authenticated', + publicReadRole = 'anonymous', +}) => { + const prepared = []; + const failures = []; + + if (!Array.isArray(targets) || targets.length === 0) { + return { prepared, failures }; + } + + if (dryRun) { + for (const target of targets) { + prepared.push({ + ...target, + dryRun: true, + publicRole, + publicReadRole, + rlsEnabled: null, + createdPolicies: [], + }); + } + return { prepared, failures }; + } + + const pool = new Pool(pgConfig); + try { + for (const target of targets) { + try { + const schemaIdent = quoteIdent(target.schemaName); + const tableIdent = quoteIdent(target.tableName); + const qualified = `${schemaIdent}.${tableIdent}`; + const publicRoleIdent = quoteIdent(publicRole); + const publicReadRoleIdent = quoteIdentOrNull(publicReadRole); + + const accessSql = [ + `grant usage on schema ${schemaIdent} to ${publicRoleIdent};`, + publicReadRoleIdent ? `grant usage on schema ${schemaIdent} to ${publicReadRoleIdent};` : null, + `grant select, insert, update, delete on table ${qualified} to ${publicRoleIdent};`, + publicReadRoleIdent ? `grant select on table ${qualified} to ${publicReadRoleIdent};` : null, + ].filter(Boolean); + + for (const sql of accessSql) { + await pool.query(sql); + } + + const rlsResult = await pool.query( + ` + select c.relrowsecurity as rls_enabled + from pg_class c + join pg_namespace n on n.oid = c.relnamespace + where n.nspname = $1 and c.relname = $2 + `, + [target.schemaName, target.tableName], + ); + const rlsEnabled = Boolean(rlsResult.rows?.[0]?.rls_enabled); + + const createdPolicies = []; + if (rlsEnabled) { + const policyNames = policyNamesForRole(publicRole); + const policyResult = await pool.query( + ` + select policyname + from pg_policies + where schemaname = $1 + and tablename = $2 + `, + [target.schemaName, target.tableName], + ); + const existingPolicies = new Set(policyResult.rows?.map((row) => row.policyname)); + + const maybeCreatePolicy = async (name, sql) => { + if (existingPolicies.has(name)) return; + await pool.query(sql); + createdPolicies.push(name); + }; + + await maybeCreatePolicy( + policyNames.select, + `create policy ${quoteIdent(policyNames.select)} on ${qualified} for select to ${publicRoleIdent} using (true);`, + ); + await maybeCreatePolicy( + policyNames.insert, + `create policy ${quoteIdent(policyNames.insert)} on ${qualified} for insert to ${publicRoleIdent} with check (true);`, + ); + await maybeCreatePolicy( + policyNames.update, + `create policy ${quoteIdent(policyNames.update)} on ${qualified} for update to ${publicRoleIdent} using (true) with check (true);`, + ); + } + + prepared.push({ + ...target, + dryRun: false, + publicRole, + publicReadRole, + rlsEnabled, + createdPolicies, + }); + } catch (error) { + failures.push({ + ...target, + phase: 'ensurePublicAccess', + error: error instanceof Error ? error.message : String(error), + }); + } + } + } finally { + await pool.end(); + } + + return { prepared, failures }; +}; diff --git a/graphql/server/perf/src/lib/args.ts b/graphql/server/perf/src/lib/args.ts new file mode 100644 index 0000000000..e227b947d5 --- /dev/null +++ b/graphql/server/perf/src/lib/args.ts @@ -0,0 +1,102 @@ +import type { ParsedArgs } from '../types'; + +export function parseArgs(raw: string[]): ParsedArgs { + const positionals: string[] = []; + const flags = new Map(); + + for (let i = 0; i < raw.length; i += 1) { + const token = raw[i]; + if (!token.startsWith('--')) { + positionals.push(token); + continue; + } + + const eq = token.indexOf('='); + if (eq !== -1) { + flags.set(token.slice(0, eq), token.slice(eq + 1)); + continue; + } + + const next = raw[i + 1]; + if (next != null && !next.startsWith('--')) { + flags.set(token, next); + i += 1; + } else { + flags.set(token, true); + } + } + + return { positionals, flags, raw }; +} + +export function hasFlag(flags: Map, name: string): boolean { + return flags.has(name) && flags.get(name) !== false; +} + +export function getStringFlag( + flags: Map, + name: string, + fallback?: string, +): string | undefined { + const value = flags.get(name); + if (typeof value === 'string') return value; + return fallback; +} + +export function getNumberFlag( + flags: Map, + name: string, + fallback: number, +): number { + const value = getStringFlag(flags, name); + if (value == null || value.length === 0) return fallback; + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : fallback; +} + +export function parseCsv(value: string | undefined, fallback: string[]): string[] { + if (value == null || value.trim().length === 0) return fallback; + return value + .split(',') + .map((item) => item.trim()) + .filter(Boolean); +} + +export function mapAliases(args: string[], aliases: Record): string[] { + const out: string[] = []; + for (let i = 0; i < args.length; i += 1) { + const token = args[i]; + if (token.startsWith('--') && token.includes('=')) { + const [name, value] = token.split(/=(.*)/s, 2); + out.push(`${aliases[name] ?? name}=${value}`); + continue; + } + out.push(aliases[token] ?? token); + } + return out; +} + +export function stripFlags(args: string[], names: string[]): string[] { + const remove = new Set(names); + const out: string[] = []; + + for (let i = 0; i < args.length; i += 1) { + const token = args[i]; + if (!token.startsWith('--')) { + out.push(token); + continue; + } + + const name = token.includes('=') ? token.slice(0, token.indexOf('=')) : token; + if (!remove.has(name)) { + out.push(token); + continue; + } + + if (!token.includes('=') && args[i + 1] != null && !args[i + 1].startsWith('--')) { + i += 1; + } + } + + return out; +} diff --git a/graphql/server/perf/src/lib/config.ts b/graphql/server/perf/src/lib/config.ts new file mode 100644 index 0000000000..e88c62f43f --- /dev/null +++ b/graphql/server/perf/src/lib/config.ts @@ -0,0 +1,76 @@ +import type { CacheMode, RoutingMode } from '../types'; + +export const DEFAULT_BASE_URL = 'http://localhost:3000'; +export const DEFAULT_PORT = 3000; + +export function oldCacheMax(k: number): number { + return Math.max(100, k * 6); +} + +export function withLocalhostNoProxy(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv { + const noProxy = env.NO_PROXY || 'localhost,127.0.0.1,::1'; + return { + ...env, + NO_PROXY: noProxy, + no_proxy: env.no_proxy || noProxy, + }; +} + +export function serverEnv({ + routingMode, + cacheMode, + port, + k, +}: { + routingMode: RoutingMode; + cacheMode: CacheMode; + port: number; + k: number; +}): NodeJS.ProcessEnv { + const env: NodeJS.ProcessEnv = withLocalhostNoProxy({ + ...process.env, + PGHOST: process.env.PGHOST || 'localhost', + PGPORT: process.env.PGPORT || '5432', + PGUSER: process.env.PGUSER || 'postgres', + PGPASSWORD: process.env.PGPASSWORD || 'password', + PGDATABASE: process.env.PGDATABASE || 'constructive', + NODE_ENV: 'development', + GRAPHILE_ENV: 'development', + GRAPHQL_OBSERVABILITY_ENABLED: 'true', + TS_NODE_TRANSPILE_ONLY: 'true', + API_IS_PUBLIC: routingMode === 'public' ? 'true' : 'false', + PORT: String(port), + }); + + if (cacheMode === 'new') { + env.USE_MULTI_TENANCY_CACHE = 'true'; + delete env.GRAPHILE_CACHE_MAX; + } else { + delete env.USE_MULTI_TENANCY_CACHE; + env.GRAPHILE_CACHE_MAX = String(oldCacheMax(k)); + } + + return env; +} + +export function redactEnv(env: NodeJS.ProcessEnv): Record { + const keys = [ + 'PGHOST', + 'PGPORT', + 'PGUSER', + 'PGPASSWORD', + 'PGDATABASE', + 'NODE_ENV', + 'GRAPHILE_ENV', + 'GRAPHQL_OBSERVABILITY_ENABLED', + 'API_IS_PUBLIC', + 'USE_MULTI_TENANCY_CACHE', + 'GRAPHILE_CACHE_MAX', + 'PORT', + 'NO_PROXY', + 'no_proxy', + ]; + return Object.fromEntries( + keys.map((key) => [key, key.toLowerCase().includes('password') ? (env[key] ? '***' : undefined) : env[key]]), + ); +} diff --git a/graphql/server/perf/src/lib/graphql.ts b/graphql/server/perf/src/lib/graphql.ts new file mode 100644 index 0000000000..1e0eac2533 --- /dev/null +++ b/graphql/server/perf/src/lib/graphql.ts @@ -0,0 +1,4 @@ +export interface GraphqlPayload { + query: string; + variables?: Record; +} diff --git a/graphql/server/perf/src/lib/http.ts b/graphql/server/perf/src/lib/http.ts new file mode 100644 index 0000000000..13fb166ceb --- /dev/null +++ b/graphql/server/perf/src/lib/http.ts @@ -0,0 +1,63 @@ +import http from 'node:http'; +import https from 'node:https'; +import type { JsonHttpResult } from '../types'; + +export async function getJson(url: string, timeoutMs = 10_000): Promise { + const startedAt = Date.now(); + return await new Promise((resolve) => { + const target = new URL(url); + const client = target.protocol === 'https:' ? https : http; + const req = client.request( + { + protocol: target.protocol, + hostname: target.hostname, + port: target.port || (target.protocol === 'https:' ? 443 : 80), + path: `${target.pathname}${target.search}`, + method: 'GET', + }, + (res) => { + const chunks: Buffer[] = []; + res.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))); + res.on('end', () => { + const text = Buffer.concat(chunks).toString('utf8'); + let json: unknown; + try { + json = JSON.parse(text); + } catch { + json = undefined; + } + const status = Number(res.statusCode ?? 0); + resolve({ + ok: status >= 200 && status < 300, + status, + elapsedMs: Date.now() - startedAt, + json, + text, + }); + }); + }, + ); + + req.on('error', (error) => { + resolve({ + ok: false, + status: 0, + elapsedMs: Date.now() - startedAt, + error: error instanceof Error ? error.message : String(error), + }); + }); + req.setTimeout(timeoutMs, () => req.destroy(new Error(`Request timeout after ${timeoutMs}ms`))); + req.end(); + }); +} + +export async function waitForJsonOk(url: string, timeoutMs: number, intervalMs = 1000): Promise { + const deadline = Date.now() + timeoutMs; + let last: JsonHttpResult | undefined; + while (Date.now() < deadline) { + last = await getJson(url, Math.min(intervalMs, 5000)); + if (last.ok) return; + await new Promise((resolve) => setTimeout(resolve, intervalMs)); + } + throw new Error(`Timed out waiting for ${url}; last=${JSON.stringify(last)}`); +} diff --git a/graphql/server/perf/src/lib/paths.ts b/graphql/server/perf/src/lib/paths.ts new file mode 100644 index 0000000000..ef669c8a80 --- /dev/null +++ b/graphql/server/perf/src/lib/paths.ts @@ -0,0 +1,12 @@ +import path from 'node:path'; +import type { PerfPaths } from '../types'; + +export function getPerfPaths(): PerfPaths { + const perfSrcDir = __dirname.endsWith(`${path.sep}lib`) + ? path.resolve(__dirname, '..') + : path.resolve(__dirname); + const perfDir = path.resolve(perfSrcDir, '..'); + const serverDir = path.resolve(perfDir, '..'); + const repoRoot = path.resolve(serverDir, '..', '..'); + return { repoRoot, serverDir, perfDir }; +} diff --git a/graphql/server/perf/src/lib/pg.ts b/graphql/server/perf/src/lib/pg.ts new file mode 100644 index 0000000000..9477ed029c --- /dev/null +++ b/graphql/server/perf/src/lib/pg.ts @@ -0,0 +1,17 @@ +export interface PgConfig { + host: string; + port: number; + database: string; + user: string; + password: string; +} + +export function pgConfigFromEnv(env: NodeJS.ProcessEnv = process.env): PgConfig { + return { + host: env.PGHOST || 'localhost', + port: Number.parseInt(env.PGPORT || '5432', 10), + database: env.PGDATABASE || 'constructive', + user: env.PGUSER || 'postgres', + password: env.PGPASSWORD || 'password', + }; +} diff --git a/graphql/server/perf/src/lib/process.ts b/graphql/server/perf/src/lib/process.ts new file mode 100644 index 0000000000..601fb83db0 --- /dev/null +++ b/graphql/server/perf/src/lib/process.ts @@ -0,0 +1,41 @@ +import { spawn } from 'node:child_process'; +import type { RunProcessOptions } from '../types'; + +const SHELL_CHARS = /[^A-Za-z0-9_/:=.,@%+-]/; + +export function shellQuote(value: string): string { + if (value.length > 0 && !SHELL_CHARS.test(value)) return value; + return `'${value.replace(/'/g, `'"'"'`)}'`; +} + +export function formatCommand(command: string, args: string[]): string { + return [command, ...args].map(shellQuote).join(' '); +} + +export async function runProcess( + command: string, + args: string[], + options: RunProcessOptions, +): Promise { + const label = options.label ? `[${options.label}] ` : ''; + console.log(`${label}${formatCommand(command, args)}`); + + if (options.dryRun) return; + + await new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd: options.cwd, + env: options.env || process.env, + stdio: 'inherit', + }); + + child.on('error', reject); + child.on('exit', (code, signal) => { + if (code === 0) { + resolve(); + return; + } + reject(new Error(`${formatCommand(command, args)} failed with code=${code} signal=${signal ?? ''}`)); + }); + }); +} diff --git a/graphql/server/perf/src/lib/public-access.ts b/graphql/server/perf/src/lib/public-access.ts new file mode 100644 index 0000000000..14dafed14c --- /dev/null +++ b/graphql/server/perf/src/lib/public-access.ts @@ -0,0 +1,220 @@ +import { Pool } from 'pg'; +import type { PgConfig } from './pg'; + +export interface BusinessProfile { + key?: string; + table?: { + physicalSchema?: string; + tableName?: string; + databaseId?: string; + }; +} + +export interface PublicAccessTarget { + schemaName: string; + tableName: string; + databaseId?: string | null; + profileKey?: string | null; +} + +export interface PublicAccessPrepared extends PublicAccessTarget { + dryRun: boolean; + publicRole: string; + publicReadRole?: string | null; + rlsEnabled: boolean | null; + createdPolicies: string[]; +} + +export interface PublicAccessFailure extends PublicAccessTarget { + phase: string; + error: string; + sql?: string; +} + +const POLICY_NAMES = { + select: 'perf_load_public_select', + insert: 'perf_load_public_insert', + update: 'perf_load_public_update', +}; + +export function quoteIdent(value: unknown): string { + return `"${String(value).replace(/"/g, '""')}"`; +} + +function quoteIdentOrNull(value: unknown): string | null { + const text = String(value ?? '').trim(); + return text.length > 0 ? quoteIdent(text) : null; +} + +function policySuffixForRole(role: string): string { + const suffix = String(role ?? '') + .trim() + .toLowerCase() + .replace(/[^a-z0-9_]+/g, '_') + .replace(/^_+|_+$/g, ''); + return suffix || 'role'; +} + +function policyNamesForRole(role: string): typeof POLICY_NAMES { + const suffix = policySuffixForRole(role); + return { + select: `${POLICY_NAMES.select}_${suffix}`, + insert: `${POLICY_NAMES.insert}_${suffix}`, + update: `${POLICY_NAMES.update}_${suffix}`, + }; +} + +export function extractProfiles(payload: unknown): BusinessProfile[] { + if (Array.isArray(payload)) return payload as BusinessProfile[]; + if (payload && typeof payload === 'object' && Array.isArray((payload as { profiles?: unknown }).profiles)) { + return (payload as { profiles: BusinessProfile[] }).profiles; + } + return []; +} + +export function buildTargetsFromProfiles(profiles: BusinessProfile[]): PublicAccessTarget[] { + const dedupe = new Map(); + + for (const profile of profiles) { + const schemaName = profile?.table?.physicalSchema; + const tableName = profile?.table?.tableName; + if (!schemaName || !tableName) continue; + dedupe.set(`${schemaName}.${tableName}`, { + schemaName, + tableName, + databaseId: profile?.table?.databaseId ?? null, + profileKey: profile?.key ?? null, + }); + } + + return [...dedupe.values()].sort((a, b) => + `${a.schemaName}.${a.tableName}`.localeCompare(`${b.schemaName}.${b.tableName}`), + ); +} + +export function getUnsafeTargets(targets: PublicAccessTarget[]): PublicAccessTarget[] { + return targets.filter((target) => !target.schemaName.startsWith('perf-')); +} + +export async function ensurePublicAccessForTargets({ + targets, + pgConfig, + dryRun = false, + publicRole = 'authenticated', + publicReadRole = 'anonymous', +}: { + targets: PublicAccessTarget[]; + pgConfig: PgConfig; + dryRun?: boolean; + publicRole?: string; + publicReadRole?: string; +}): Promise<{ prepared: PublicAccessPrepared[]; failures: PublicAccessFailure[] }> { + const prepared: PublicAccessPrepared[] = []; + const failures: PublicAccessFailure[] = []; + + if (!Array.isArray(targets) || targets.length === 0) { + return { prepared, failures }; + } + + if (dryRun) { + for (const target of targets) { + prepared.push({ + ...target, + dryRun: true, + publicRole, + publicReadRole, + rlsEnabled: null, + createdPolicies: [], + }); + } + return { prepared, failures }; + } + + const pool = new Pool(pgConfig); + try { + for (const target of targets) { + try { + const schemaIdent = quoteIdent(target.schemaName); + const tableIdent = quoteIdent(target.tableName); + const qualified = `${schemaIdent}.${tableIdent}`; + const publicRoleIdent = quoteIdent(publicRole); + const publicReadRoleIdent = quoteIdentOrNull(publicReadRole); + + const accessSql = [ + `grant usage on schema ${schemaIdent} to ${publicRoleIdent};`, + publicReadRoleIdent ? `grant usage on schema ${schemaIdent} to ${publicReadRoleIdent};` : null, + `grant select, insert, update, delete on table ${qualified} to ${publicRoleIdent};`, + publicReadRoleIdent ? `grant select on table ${qualified} to ${publicReadRoleIdent};` : null, + ].filter((sql): sql is string => Boolean(sql)); + + for (const sql of accessSql) { + await pool.query(sql); + } + + const rlsResult = await pool.query<{ rls_enabled: boolean }>( + ` + select c.relrowsecurity as rls_enabled + from pg_class c + join pg_namespace n on n.oid = c.relnamespace + where n.nspname = $1 and c.relname = $2 + `, + [target.schemaName, target.tableName], + ); + const rlsEnabled = Boolean(rlsResult.rows?.[0]?.rls_enabled); + const createdPolicies: string[] = []; + + if (rlsEnabled) { + const policyNames = policyNamesForRole(publicRole); + const policyResult = await pool.query<{ policyname: string }>( + ` + select policyname + from pg_policies + where schemaname = $1 + and tablename = $2 + `, + [target.schemaName, target.tableName], + ); + const existingPolicies = new Set(policyResult.rows?.map((row) => row.policyname)); + + const maybeCreatePolicy = async (name: string, sql: string) => { + if (existingPolicies.has(name)) return; + await pool.query(sql); + createdPolicies.push(name); + }; + + await maybeCreatePolicy( + policyNames.select, + `create policy ${quoteIdent(policyNames.select)} on ${qualified} for select to ${publicRoleIdent} using (true);`, + ); + await maybeCreatePolicy( + policyNames.insert, + `create policy ${quoteIdent(policyNames.insert)} on ${qualified} for insert to ${publicRoleIdent} with check (true);`, + ); + await maybeCreatePolicy( + policyNames.update, + `create policy ${quoteIdent(policyNames.update)} on ${qualified} for update to ${publicRoleIdent} using (true) with check (true);`, + ); + } + + prepared.push({ + ...target, + dryRun: false, + publicRole, + publicReadRole, + rlsEnabled, + createdPolicies, + }); + } catch (error) { + failures.push({ + ...target, + phase: 'ensurePublicAccess', + error: error instanceof Error ? error.message : String(error), + }); + } + } + } finally { + await pool.end(); + } + + return { prepared, failures }; +} diff --git a/graphql/server/perf/src/lib/reports.ts b/graphql/server/perf/src/lib/reports.ts new file mode 100644 index 0000000000..dec7e3cb6c --- /dev/null +++ b/graphql/server/perf/src/lib/reports.ts @@ -0,0 +1,7 @@ +export function summarizeError(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} + +export function nowIso(): string { + return new Date().toISOString(); +} diff --git a/graphql/server/perf/src/lib/run-dir.ts b/graphql/server/perf/src/lib/run-dir.ts new file mode 100644 index 0000000000..f044738a83 --- /dev/null +++ b/graphql/server/perf/src/lib/run-dir.ts @@ -0,0 +1,39 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; + +export const DEFAULT_TMP_ROOT = '/tmp/constructive-perf'; + +export function toIsoFileTime(date = new Date()): string { + return date.toISOString().replace(/[:.]/g, '-'); +} + +export function makeRunId(prefix = 'graphile-cache-perf'): string { + return `${prefix}-${toIsoFileTime(new Date())}-pid${process.pid}`; +} + +export function defaultRunDir(prefix = 'graphile-cache-perf'): string { + return path.join(DEFAULT_TMP_ROOT, makeRunId(prefix)); +} + +export async function ensureRunDirs(runDir: string): Promise<{ + runDir: string; + dataDir: string; + logsDir: string; + reportsDir: string; + tmpScriptsDir: string; +}> { + const dirs = { + runDir, + dataDir: path.join(runDir, 'data'), + logsDir: path.join(runDir, 'logs'), + reportsDir: path.join(runDir, 'reports'), + tmpScriptsDir: path.join(runDir, 'tmp-scripts'), + }; + await Promise.all(Object.values(dirs).map((dir) => fs.mkdir(dir, { recursive: true }))); + return dirs; +} + +export async function writeJson(filePath: string, payload: unknown): Promise { + await fs.mkdir(path.dirname(filePath), { recursive: true }); + await fs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8'); +} diff --git a/graphql/server/perf/src/register-workspace-packages.cjs b/graphql/server/perf/src/register-workspace-packages.cjs new file mode 100644 index 0000000000..117624791a --- /dev/null +++ b/graphql/server/perf/src/register-workspace-packages.cjs @@ -0,0 +1,85 @@ +const fs = require('node:fs'); +const Module = require('node:module'); +const path = require('node:path'); + +const repoRoot = path.resolve(__dirname, '..', '..', '..', '..'); +const searchRoots = [ + 'agentic', + 'graphile', + 'graphql', + 'jobs', + 'packages', + 'pgpm', + 'postgres', + 'sdk', + 'uploads', +]; +const ignoredDirs = new Set(['.git', 'dist', 'node_modules']); + +const packageMap = new Map(); + +function walk(dir) { + if (!fs.existsSync(dir)) return; + + const packageJson = path.join(dir, 'package.json'); + if (fs.existsSync(packageJson)) { + try { + const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf8')); + if (pkg.name && !packageMap.has(pkg.name)) { + packageMap.set(pkg.name, dir); + } + } catch { + // Ignore malformed package metadata in unrelated fixtures. + } + return; + } + + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (!entry.isDirectory() || ignoredDirs.has(entry.name)) continue; + walk(path.join(dir, entry.name)); + } +} + +for (const root of searchRoots) { + walk(path.join(repoRoot, root)); +} + +function packageEntry(packageDir) { + const sourceEntry = path.join(packageDir, 'src', 'index.ts'); + if (fs.existsSync(sourceEntry)) return sourceEntry; + + const distEntry = path.join(packageDir, 'dist', 'index.js'); + if (fs.existsSync(distEntry)) return distEntry; + + const rootEntry = path.join(packageDir, 'index.js'); + if (fs.existsSync(rootEntry)) return rootEntry; + + return null; +} + +const originalResolveFilename = Module._resolveFilename; + +Module._resolveFilename = function resolveWorkspacePackage(request, parent, isMain, options) { + const packageDir = packageMap.get(request); + if (packageDir) { + const entry = packageEntry(packageDir); + if (entry) return entry; + } + + try { + return originalResolveFilename.call(this, request, parent, isMain, options); + } catch (error) { + const parentDir = parent?.filename ? path.dirname(parent.filename) : process.cwd(); + if ( + error && + error.code === 'MODULE_NOT_FOUND' && + (request.startsWith('./') || request.startsWith('../')) && + request.endsWith('.js') + ) { + const tsRequest = path.resolve(parentDir, `${request.slice(0, -3)}.ts`); + if (fs.existsSync(tsRequest)) return tsRequest; + } + + throw error; + } +}; diff --git a/graphql/server/perf/src/types.ts b/graphql/server/perf/src/types.ts new file mode 100644 index 0000000000..b3708d1781 --- /dev/null +++ b/graphql/server/perf/src/types.ts @@ -0,0 +1,82 @@ +export type CacheMode = 'old' | 'new'; +export type RoutingMode = 'private' | 'public'; + +export interface PerfPaths { + repoRoot: string; + serverDir: string; + perfDir: string; +} + +export interface ParsedArgs { + positionals: string[]; + flags: Map; + raw: string[]; +} + +export interface CommandContext { + args: string[]; + paths: PerfPaths; + dryRun: boolean; +} + +export interface CommandDefinition { + name: string; + summary: string; + usage?: string; + run: (ctx: CommandContext) => Promise; +} + +export interface PerfCliOptions { + paths?: PerfPaths; + stdout?: NodeJS.WritableStream; + stderr?: NodeJS.WritableStream; +} + +export interface RunProcessOptions { + cwd: string; + env?: NodeJS.ProcessEnv; + dryRun?: boolean; + label?: string; +} + +export interface JsonHttpResult { + ok: boolean; + status: number; + elapsedMs: number; + json?: unknown; + text?: string; + error?: string; +} + +export interface E2eMatrixResetResult { + after: string; + before: string; + ok: boolean; + reportPath: string; + failureCount?: number; + error?: string; +} + +export interface E2eMatrixCaseResult { + routingMode: RoutingMode; + cacheMode: CacheMode; + ok: boolean; + startedAt: string; + finishedAt: string; + resultPath?: string; + reportExists?: boolean; + memoryBeforePath?: string; + memoryBeforeOk?: boolean; + memoryAfterPath?: string; + memoryAfterOk?: boolean; + errors?: number; + failed?: number; + totalRequests?: number; + qps?: number; + p95Ms?: number | null; + p99Ms?: number | null; + heapDeltaMb?: number; + resetAfter?: E2eMatrixResetResult; + hardGateFailures?: string[]; + error?: string; +} diff --git a/graphql/server/perf/src/utils/display.ts b/graphql/server/perf/src/utils/display.ts new file mode 100644 index 0000000000..01e1ea6018 --- /dev/null +++ b/graphql/server/perf/src/utils/display.ts @@ -0,0 +1,47 @@ +import type { CommandDefinition } from '../types'; + +export function createUsageText(commands: CommandDefinition[]): string { + const maxNameLength = Math.max(...commands.map((command) => command.name.length)); + const commandRows = commands + .map((command) => ` ${command.name.padEnd(maxNameLength)} ${command.summary}`) + .join('\n'); + + return `Constructive GraphQL Server Perf CLI + +Usage: + pnpm --dir graphql/server perf [options] + +Commands: +${commandRows} + +Global Options: + --dry-run Print delegated commands without executing them + --help, -h Show this help + +Individual Command Help: + pnpm --dir graphql/server perf --help + +Examples: + pnpm --dir graphql/server perf private-benchmark --mode new --k 2 --duration-seconds 5 --workers 1 + pnpm --dir graphql/server perf public-preflight --run-dir /tmp/constructive-perf/dbpm-smoke + pnpm --dir graphql/server perf e2e-matrix --routing-modes private,public --cache-modes old,new --k 10 --duration-seconds 300 --workers 4 --manage-server`; +} + +export function createCommandUsageText(command: CommandDefinition): string { + if (command.usage) return command.usage; + + return `Constructive GraphQL Server Perf CLI + +Command: + perf ${command.name} + +Summary: + ${command.summary} + +Usage: + pnpm --dir graphql/server perf ${command.name} [options] + +Global Options: + --dry-run Print delegated commands without executing them + --help, -h Show this help`; +} diff --git a/graphql/server/perf/src/utils/index.ts b/graphql/server/perf/src/utils/index.ts new file mode 100644 index 0000000000..1573ad3641 --- /dev/null +++ b/graphql/server/perf/src/utils/index.ts @@ -0,0 +1 @@ +export { createCommandUsageText, createUsageText } from './display'; diff --git a/graphql/server/perf/tsconfig.json b/graphql/server/perf/tsconfig.json new file mode 100644 index 0000000000..ca2559c85f --- /dev/null +++ b/graphql/server/perf/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "rootDir": "src", + "outDir": "dist", + "declaration": false, + "sourceMap": false, + "types": ["node"], + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/graphql/server/src/diagnostics/debug-memory-snapshot.ts b/graphql/server/src/diagnostics/debug-memory-snapshot.ts index b3491ee133..86d2c4b440 100644 --- a/graphql/server/src/diagnostics/debug-memory-snapshot.ts +++ b/graphql/server/src/diagnostics/debug-memory-snapshot.ts @@ -2,6 +2,7 @@ import os from 'node:os'; import v8 from 'node:v8'; import { svcCache, SVC_CACHE_TTL_MS } from '@pgpmjs/server-utils'; import { getCacheStats } from 'graphile-cache'; +import { getMultiTenancyCacheStats } from 'graphile-multi-tenancy-cache'; import { getInFlightCount, getInFlightKeys } from '../middleware/graphile'; import { getGraphileBuildStats } from '../middleware/observability/graphile-build-stats'; @@ -41,6 +42,7 @@ export interface DebugMemorySnapshot { }>; }; graphileCache: ReturnType; + multiTenancyCache: ReturnType; svcCache: { size: number; max: number; @@ -95,6 +97,7 @@ export const getDebugMemorySnapshot = (): DebugMemorySnapshot => { heapSpaces, }, graphileCache: getCacheStats(), + multiTenancyCache: getMultiTenancyCacheStats(), svcCache: { size: svcCache.size, max: svcCache.max, diff --git a/graphql/server/src/index.ts b/graphql/server/src/index.ts index 53dd89cc1e..c95f99de87 100644 --- a/graphql/server/src/index.ts +++ b/graphql/server/src/index.ts @@ -4,5 +4,5 @@ export * from './server'; export { createApiMiddleware, getSubdomain, getApiConfig } from './middleware/api'; export { createAuthenticateMiddleware } from './middleware/auth'; export { cors } from './middleware/cors'; -export { graphile } from './middleware/graphile'; -export { flush, flushService } from './middleware/flush'; +export { graphile, multiTenancyHandler, isMultiTenancyCacheEnabled, shutdownMultiTenancy } from './middleware/graphile'; +export { flush, createFlushMiddleware, flushService } from './middleware/flush'; diff --git a/graphql/server/src/middleware/auth.ts b/graphql/server/src/middleware/auth.ts index b86e765c89..5a099edece 100644 --- a/graphql/server/src/middleware/auth.ts +++ b/graphql/server/src/middleware/auth.ts @@ -35,7 +35,7 @@ export const createAuthenticateMiddleware = ( next: NextFunction ): Promise => { const api = req.api; - log.info(`[auth] middleware called, api=${api ? 'present' : 'missing'}`); + log.debug(`[auth] middleware called, api=${api ? 'present' : 'missing'}`); if (!api) { res.status(500).send('Missing API info'); return; @@ -47,7 +47,7 @@ export const createAuthenticateMiddleware = ( }); const rlsModule = api.rlsModule; - log.info( + log.debug( `[auth] rlsModule=${rlsModule ? 'present' : 'missing'}, ` + `authenticate=${rlsModule?.authenticate ?? 'none'}, ` + `authenticateStrict=${rlsModule?.authenticateStrict ?? 'none'}, ` + @@ -55,7 +55,7 @@ export const createAuthenticateMiddleware = ( ); if (!rlsModule) { - log.info('[auth] No RLS module configured, skipping auth'); + log.debug('[auth] No RLS module configured, skipping auth'); return next(); } @@ -63,7 +63,7 @@ export const createAuthenticateMiddleware = ( ? rlsModule.authenticateStrict : rlsModule.authenticate; - log.info( + log.debug( `[auth] strictAuth=${opts.server?.strictAuth ?? false}, authFn=${authFn ?? 'none'}` ); @@ -72,7 +72,7 @@ export const createAuthenticateMiddleware = ( const [authType, authToken] = authorization.split(' '); let token: any = {}; - log.info( + log.debug( `[auth] authorization header present=${!!authorization}, ` + `authType=${authType ?? 'none'}, hasToken=${!!authToken}` ); @@ -85,7 +85,7 @@ export const createAuthenticateMiddleware = ( const tokenSource = (authType?.toLowerCase() === 'bearer' && authToken) ? 'bearer' : (cookieToken ? 'cookie' : 'none'); if (effectiveToken) { - log.info(`[auth] Processing ${tokenSource} authentication`); + log.debug(`[auth] Processing ${tokenSource} authentication`); const context: Record = { 'jwt.claims.ip_address': req.clientIp, }; @@ -98,7 +98,7 @@ export const createAuthenticateMiddleware = ( } const authQuery = `SELECT * FROM "${rlsModule.privateSchema.schemaName}"."${authFn}"($1)`; - log.info(`[auth] Executing auth query: ${authQuery}`); + log.debug(`[auth] Executing auth query: ${authQuery}`); try { const result = await pgQueryContext({ @@ -108,10 +108,10 @@ export const createAuthenticateMiddleware = ( variables: [effectiveToken], }); - log.info(`[auth] Query result: rowCount=${result?.rowCount}`); + log.debug(`[auth] Query result: rowCount=${result?.rowCount}`); if (result?.rowCount === 0) { - log.info('[auth] No rows returned, returning UNAUTHENTICATED'); + log.debug('[auth] No rows returned, returning UNAUTHENTICATED'); res.status(200).json({ errors: [{ extensions: { code: 'UNAUTHENTICATED' } }], }); @@ -119,7 +119,7 @@ export const createAuthenticateMiddleware = ( } token = result.rows[0]; - log.info(`[auth] Auth success: role=${token.role}, user_id=${token.user_id}`); + log.debug(`[auth] Auth success: role=${token.role}, user_id=${token.user_id}`); } catch (e: any) { log.error('[auth] Auth error:', e.message); res.status(200).json({ @@ -135,12 +135,12 @@ export const createAuthenticateMiddleware = ( return; } } else { - log.info('[auth] No credential provided (no bearer token or session cookie), using anonymous auth'); + log.debug('[auth] No credential provided (no bearer token or session cookie), using anonymous auth'); } req.token = token; } else { - log.info( + log.debug( `[auth] Skipping auth: authFn=${authFn ?? 'none'}, ` + `privateSchema=${rlsModule.privateSchema?.schemaName ?? 'none'}` ); @@ -150,7 +150,7 @@ export const createAuthenticateMiddleware = ( const deviceToken = parseCookieToken(req, DEVICE_TOKEN_COOKIE_NAME); if (deviceToken) { req.deviceToken = deviceToken; - log.info('[auth] Device token cookie present'); + log.debug('[auth] Device token cookie present'); } next(); diff --git a/graphql/server/src/middleware/flush.ts b/graphql/server/src/middleware/flush.ts index 653d74631c..77aa5fba36 100644 --- a/graphql/server/src/middleware/flush.ts +++ b/graphql/server/src/middleware/flush.ts @@ -3,8 +3,13 @@ import { Logger } from '@pgpmjs/logger'; import { svcCache } from '@pgpmjs/server-utils'; import { NextFunction, Request, Response } from 'express'; import { graphileCache } from 'graphile-cache'; +import { + flushTenantInstance, + flushByDatabaseId, +} from 'graphile-multi-tenancy-cache'; import { getPgPool } from 'pg-cache'; import './types'; // for Request type +import { isMultiTenancyCacheEnabled } from './graphile'; const log = new Logger('flush'); @@ -23,11 +28,39 @@ export const flush = async ( return next(); }; +/** + * Enhanced flush middleware with multi-tenancy cache support. + * Replaces flush() when multi-tenancy cache is enabled. + */ +export const createFlushMiddleware = (opts: ConstructiveOptions) => { + const multiTenancyEnabled = isMultiTenancyCacheEnabled(opts); + + return async (req: Request, res: Response, next: NextFunction): Promise => { + if (req.url === '/flush') { + const key = (req as any).svc_key; + + // Standard cache flush + graphileCache.delete(key); + svcCache.delete(key); + + // Multi-tenancy cache flush + if (multiTenancyEnabled && key) { + flushTenantInstance(key); + } + + res.status(200).send('OK'); + return; + } + return next(); + }; +}; + export const flushService = async ( opts: ConstructiveOptions, databaseId: string ): Promise => { const pgPool = getPgPool(opts.pg); + const multiTenancyEnabled = isMultiTenancyCacheEnabled(opts); log.info('flushing db ' + databaseId); const api = new RegExp(`^api:${databaseId}:.*`); @@ -43,6 +76,11 @@ export const flushService = async ( }); } + // Flush all handlers associated with this databaseId via index + if (multiTenancyEnabled) { + flushByDatabaseId(databaseId); + } + const svc = await pgPool.query( `SELECT * FROM services_public.domains @@ -62,6 +100,11 @@ export const flushService = async ( if (key) { graphileCache.delete(key); svcCache.delete(key); + + // Multi-tenancy cache: flush tenant instance + if (multiTenancyEnabled) { + flushTenantInstance(key); + } } } }; diff --git a/graphql/server/src/middleware/graphile.ts b/graphql/server/src/middleware/graphile.ts index e9fcd62001..e2e5523f0f 100644 --- a/graphql/server/src/middleware/graphile.ts +++ b/graphql/server/src/middleware/graphile.ts @@ -9,6 +9,13 @@ import type { GraphileConfig } from 'graphile-config'; import { createConstructivePreset, makePgService } from 'graphile-settings'; import { getPgPool } from 'pg-cache'; import { getPgEnvOptions } from 'pg-env'; +import type { Pool } from 'pg'; +import { + configureMultiTenancyCache, + getTenantInstance, + getOrCreateTenantInstance, + shutdownMultiTenancyCache, +} from 'graphile-multi-tenancy-cache'; import './types'; // for Request type import { isGraphqlObservabilityEnabled } from '../diagnostics/observability'; import { HandlerCreationError } from '../errors/api-errors'; @@ -203,7 +210,7 @@ const reqLabel = (req: Request): string => (req.requestId ? `[${req.requestId}]` * (everything on except aggregates). */ const buildPreset = ( - pool: import('pg').Pool, + pool: Pool, schemas: string[], anonRole: string, roleName: string, @@ -299,7 +306,47 @@ const buildPreset = ( }; }; -export const graphile = (opts: ConstructiveOptions): RequestHandler => { +interface CreateTenantGraphileResourcesOptions { + pool: Pool; + schemas: string[]; + anonRole: string; + roleName: string; + databaseSettings?: DatabaseSettings; + cacheKey: string; + serviceKey: string; + databaseId?: string | null; + observabilityEnabled: boolean; +} + +const createTenantGraphileResources = ({ + pool, + schemas, + anonRole, + roleName, + databaseSettings, + cacheKey, + serviceKey, + databaseId, + observabilityEnabled, +}: CreateTenantGraphileResourcesOptions): Promise => { + const preset = buildPreset(pool, schemas, anonRole, roleName, databaseSettings); + return observeGraphileBuild( + { + cacheKey, + serviceKey, + databaseId: databaseId ?? null, + }, + () => + createGraphileInstance({ + preset, + cacheKey, + enableRealtime: databaseSettings?.enableRealtime, + }), + { enabled: observabilityEnabled }, + ); +}; + +const routeKeyGraphile = (opts: ConstructiveOptions): RequestHandler => { const observabilityEnabled = isGraphqlObservabilityEnabled(opts.server?.host); return async (req: Request, res: Response, next: NextFunction) => { @@ -377,20 +424,17 @@ export const graphile = (opts: ConstructiveOptions): RequestHandler => { const pool = getPgPool(pgConfig); // Create promise and store in in-flight map BEFORE try block - const preset = buildPreset(pool, schema || [], anonRole, roleName, api.databaseSettings); - const creationPromise = observeGraphileBuild( - { - cacheKey: key, - serviceKey: key, - databaseId: api.databaseId ?? null, - }, - () => createGraphileInstance({ - preset, - cacheKey: key, - enableRealtime: api.databaseSettings?.enableRealtime, - }), - { enabled: observabilityEnabled }, - ); + const creationPromise = createTenantGraphileResources({ + pool, + schemas: schema || [], + anonRole, + roleName, + databaseSettings: api.databaseSettings, + cacheKey: key, + serviceKey: key, + databaseId: api.databaseId, + observabilityEnabled, + }); creating.set(key, creationPromise); try { @@ -422,3 +466,132 @@ export const graphile = (opts: ConstructiveOptions): RequestHandler => { } }; }; + +// ============================================================================= +// Multi-Tenancy Cache Handler +// ============================================================================= + +/** + * Check if multi-tenancy cache is enabled for these options. + */ +export function isMultiTenancyCacheEnabled(opts: ConstructiveOptions): boolean { + return opts.api?.useMultiTenancyCache === true; +} + +/** + * Shutdown multi-tenancy cache resources. + */ +export async function shutdownMultiTenancy(): Promise { + await shutdownMultiTenancyCache(); +} + +/** + * Multi-tenancy cache handler. + * + * Selected when opts.api.useMultiTenancyCache === true. + * Calls configureMultiTenancyCache() once at startup (package owns wrapping). + * Uses getTenantInstance() for fast-path cache hit. + * On miss, calls getOrCreateTenantInstance(); the package handles buildKey + * dedupe while this server injects the concrete Graphile handler factory. + * Routes request to tenant.handler. + */ +export const multiTenancyHandler = (opts: ConstructiveOptions): RequestHandler => { + const observabilityEnabled = isGraphqlObservabilityEnabled(opts.server?.host); + + // One-time bootstrap: configure the multi-tenancy cache with our handler factory + configureMultiTenancyCache({ + handlerFactory: async ({ + buildKey, + svcKey, + pool, + schemas, + anonRole, + roleName, + databaseId, + presetOptions, + }) => { + const databaseSettings = presetOptions as DatabaseSettings | undefined; + return createTenantGraphileResources({ + pool, + schemas, + anonRole, + roleName, + databaseSettings, + cacheKey: buildKey, + serviceKey: svcKey, + databaseId, + observabilityEnabled, + }); + }, + }); + + log.info('Multi-tenancy cache handler initialized'); + + return async (req: Request, res: Response, next: NextFunction) => { + const label = reqLabel(req); + try { + const api = req.api; + if (!api) { + log.error(`${label} Missing API info`); + return res.status(500).send('Missing API info'); + } + const key = req.svc_key; + if (!key) { + log.error(`${label} Missing service cache key`); + return res.status(500).send('Missing service cache key'); + } + const { dbname, anonRole, roleName, schema } = api; + const schemaLabel = schema?.join(',') || 'unknown'; + + // Fast path: check tenant instance cache + const cached = getTenantInstance(key); + if (cached) { + log.debug(`${label} Multi-tenancy cache hit key=${key} db=${dbname} schemas=${schemaLabel}`); + return cached.handler(req, res, next); + } + + log.debug(`${label} Multi-tenancy cache miss key=${key} db=${dbname} schemas=${schemaLabel}`); + + // Cold path: create or coalesce tenant instance + const pgConfig = getPgEnvOptions({ + ...opts.pg, + database: dbname, + }); + const pool = getPgPool(pgConfig); + + const tenant = await getOrCreateTenantInstance({ + svcKey: key, + pool, + schemas: schema || [], + anonRole, + roleName, + databaseId: api.databaseId, + presetOptions: api.databaseSettings, + }); + + return tenant.handler(req, res, next); + } catch (e: any) { + log.error(`${label} Multi-tenancy middleware error`, e); + if (!res.headersSent) { + return res.status(500).json({ + error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } + }); + } + next(e); + } + }; +}; + +/** + * Graphile middleware facade. + * + * `server.ts` mounts this once; this module owns the choice between the + * legacy route-key cache and the buildKey-based multi-tenancy cache. + */ +export const graphile = (opts: ConstructiveOptions): RequestHandler => { + if (isMultiTenancyCacheEnabled(opts)) { + log.info('Multi-tenancy cache ENABLED'); + return multiTenancyHandler(opts); + } + return routeKeyGraphile(opts); +}; diff --git a/graphql/server/src/server.ts b/graphql/server/src/server.ts index a88b48d9f4..730ea96945 100644 --- a/graphql/server/src/server.ts +++ b/graphql/server/src/server.ts @@ -6,7 +6,13 @@ import { healthz, poweredBy, svcCache, trustProxy } from '@pgpmjs/server-utils'; import { PgpmOptions } from '@pgpmjs/types'; import { middleware as parseDomains } from '@constructive-io/url-domains'; import cookieParser from 'cookie-parser'; -import express, { Express, NextFunction, Request, RequestHandler, Response } from 'express'; +import express, { + Express, + NextFunction, + Request, + RequestHandler, + Response, +} from 'express'; import type { Server as HttpServer } from 'http'; import graphqlUpload from 'graphql-upload'; import { Pool, PoolClient } from 'pg'; @@ -27,8 +33,11 @@ import { createAuthenticateMiddleware } from './middleware/auth'; import { cors } from './middleware/cors'; import { errorHandler, notFoundHandler } from './middleware/error-handler'; import { favicon } from './middleware/favicon'; -import { flush, flushService } from './middleware/flush'; -import { graphile } from './middleware/graphile'; +import { createFlushMiddleware, flushService } from './middleware/flush'; +import { + graphile, + shutdownMultiTenancy, +} from './middleware/graphile'; import { multipartBridge } from './middleware/multipart-bridge'; import { createDebugDatabaseMiddleware } from './middleware/observability/debug-db'; import { debugMemory } from './middleware/observability/debug-memory'; @@ -38,7 +47,10 @@ import { createRequestLogger } from './middleware/observability/request-logger'; import { createCaptchaMiddleware } from './middleware/captcha'; import { parseCookieValue, SESSION_COOKIE_NAME } from './middleware/cookie'; import { createAgenticRouter } from 'agentic-server'; -import { createContextMiddleware, requestIdMiddleware } from '@constructive-io/express-context'; +import { + createContextMiddleware, + requestIdMiddleware, +} from '@constructive-io/express-context'; import { startDebugSampler } from './diagnostics/debug-sampler'; const log = new Logger('server'); @@ -64,7 +76,9 @@ const log = new Logger('server'); * GraphQLServer(pgpmOptions); * ``` */ -export const GraphQLServer = (rawOpts: ConstructiveOptions | PgpmOptions = {}) => { +export const GraphQLServer = ( + rawOpts: ConstructiveOptions | PgpmOptions = {} +) => { const opts = getEnvOptions(rawOpts); const app = new Server(opts); app.addEventListener(); @@ -85,7 +99,9 @@ class Server { this.opts = getEnvOptions(opts); const effectiveOpts = this.opts; const observabilityRequested = isGraphqlObservabilityRequested(); - const observabilityEnabled = isGraphqlObservabilityEnabled(effectiveOpts.server?.host); + const observabilityEnabled = isGraphqlObservabilityEnabled( + effectiveOpts.server?.host + ); const app = express(); const api = createApiMiddleware(effectiveOpts); @@ -106,6 +122,7 @@ class Server { exposedSchemas: apiOpts.exposedSchemas?.join(',') || 'none', anonRole: apiOpts.anonRole, roleName: apiOpts.roleName, + useMultiTenancyCache: apiOpts.useMultiTenancyCache, observabilityEnabled, }); @@ -121,14 +138,18 @@ class Server { log.warn( `GRAPHQL_OBSERVABILITY_ENABLED was requested but observability remains disabled${ reasons.length > 0 ? `: ${reasons.join('; ')}` : '' - }`, + }` ); } healthz(app); if (observabilityEnabled) { app.get('/debug/memory', localObservabilityOnly, debugMemory); - app.get('/debug/db', localObservabilityOnly, createDebugDatabaseMiddleware(effectiveOpts)); + app.get( + '/debug/db', + localObservabilityOnly, + createDebugDatabaseMiddleware(effectiveOpts) + ); } else { app.use('/debug', (_req, res) => { res.status(404).send('Not found'); @@ -141,20 +162,25 @@ class Server { if (fallbackOrigin && process.env.NODE_ENV === 'production') { if (fallbackOrigin === '*') { log.warn( - 'CORS wildcard ("*") is enabled in production; this effectively disables CORS and is not recommended. Prefer per-API CORS via meta schema.', + 'CORS wildcard ("*") is enabled in production; this effectively disables CORS and is not recommended. Prefer per-API CORS via meta schema.' ); } else { - log.warn(`CORS override origin set to ${fallbackOrigin} in production. Prefer per-API CORS via meta schema.`); + log.warn( + `CORS override origin set to ${fallbackOrigin} in production. Prefer per-API CORS via meta schema.` + ); } } app.use(poweredBy('constructive')); app.use(cookieParser()); app.use(cors(fallbackOrigin)); - app.use('/graphql', graphqlUpload.graphqlUploadExpress({ - maxFileSize: 10 * 1024 * 1024, // 10 MB - maxFiles: 10, - })); + app.use( + '/graphql', + graphqlUpload.graphqlUploadExpress({ + maxFileSize: 10 * 1024 * 1024, // 10 MB + maxFiles: 10, + }) + ); // Rewrite Content-Type after graphql-upload so grafserv accepts the request app.use('/graphql', multipartBridge); @@ -176,7 +202,11 @@ class Server { sameSite: 'lax', }, }); - const csrfProtect: RequestHandler = (req: Request, res: Response, next: NextFunction) => { + const csrfProtect: RequestHandler = ( + req: Request, + res: Response, + next: NextFunction + ) => { // Skip CSRF for Bearer token auth const auth = req.headers.authorization; if (auth?.toLowerCase().startsWith('bearer ')) { @@ -190,7 +220,11 @@ class Server { // Apply CSRF protection for cookie-authenticated requests csrf.protect(req as any, res as any, next); }; - const csrfSetToken: RequestHandler = (req: Request, res: Response, next: NextFunction) => { + const csrfSetToken: RequestHandler = ( + req: Request, + res: Response, + next: NextFunction + ) => { csrf.setToken(req as any, res as any, next); }; app.use(csrfSetToken); // Set CSRF token cookie on all requests @@ -201,20 +235,22 @@ class Server { app.use(createAgenticRouter()); app.use(graphile(effectiveOpts)); - app.use(flush); + app.use(createFlushMiddleware(effectiveOpts)); // Error handling - MUST be LAST app.use(notFoundHandler); // Catches unmatched routes (404) app.use(errorHandler); // Catches all thrown errors this.app = app; - this.debugSampler = observabilityEnabled ? startDebugSampler(effectiveOpts) : null; + this.debugSampler = observabilityEnabled + ? startDebugSampler(effectiveOpts) + : null; } listen(): HttpServer { const { server } = this.opts; const httpServer = this.app.listen(server?.port, server?.host, () => - log.info(`listening at http://${server?.host}:${server?.port}`), + log.info(`listening at http://${server?.host}:${server?.port}`) ); httpServer.on('error', (err: NodeJS.ErrnoException) => { @@ -244,7 +280,11 @@ class Server { pgPool.connect(this.listenForChanges.bind(this)); } - listenForChanges(err: Error | null, client: PoolClient, release: () => void): void { + listenForChanges( + err: Error | null, + client: PoolClient, + release: () => void + ): void { if (err) { this.error('Error connecting with notify listener', err); if (!this.shuttingDown) { @@ -321,7 +361,9 @@ class Server { this.debugSampler = null; } if (this.httpServer?.listening) { - await new Promise((resolve) => this.httpServer!.close(() => resolve())); + await new Promise((resolve) => + this.httpServer!.close(() => resolve()) + ); } await closeDebugDatabasePools(); if (closeCaches) { @@ -332,6 +374,8 @@ class Server { static async closeCaches(opts: { closePools?: boolean } = {}): Promise { const { closePools = false } = opts; svcCache.clear(); + // Shutdown multi-tenancy cache if it was enabled + await shutdownMultiTenancy(); // Use closeAllCaches to properly await async disposal of PostGraphile instances // before closing pg pools - this ensures all connections are released if (closePools) { diff --git a/graphql/types/src/graphile.ts b/graphql/types/src/graphile.ts index 357f201402..fa6b542a03 100644 --- a/graphql/types/src/graphile.ts +++ b/graphql/types/src/graphile.ts @@ -42,6 +42,8 @@ export interface ApiOptions { isPublic?: boolean; /** Schemas containing metadata tables */ metaSchemas?: string[]; + /** Enable multi-tenancy cache (buildKey-based handler reuse across matching inputs) */ + useMultiTenancyCache?: boolean; } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a786de45ff..990d54cfcd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,7 @@ overrides: packageExtensionsChecksum: sha256-x8B4zkJ4KLRX+yspUWxuggXWlz6zrBLSIh72pNhpPiE= importers: + .: devDependencies: '@jest/test-sequencer': @@ -611,6 +612,35 @@ importers: version: link:../../postgres/pgsql-test/dist publishDirectory: dist + graphile/graphile-multi-tenancy-cache: + dependencies: + '@pgpmjs/logger': + specifier: workspace:^ + version: link:../../pgpm/logger/dist + express: + specifier: ^5.2.1 + version: 5.2.1 + lru-cache: + specifier: ^11.2.7 + version: 11.2.7 + pg: + specifier: ^8.11.3 + version: 8.21.0 + devDependencies: + '@types/express': + specifier: ^5.0.6 + version: 5.0.6 + '@types/pg': + specifier: ^8.10.9 + version: 8.20.0 + makage: + specifier: ^0.3.0 + version: 0.3.0 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@25.9.1)(typescript@5.9.3) + publishDirectory: dist + graphile/graphile-pg-aggregates: dependencies: '@dataplan/pg': @@ -1712,6 +1742,9 @@ importers: graphile-config: specifier: 1.0.1 version: 1.0.1 + graphile-multi-tenancy-cache: + specifier: workspace:^ + version: link:../../graphile/graphile-multi-tenancy-cache/dist graphile-settings: specifier: workspace:^ version: link:../../graphile/graphile-settings/dist @@ -3555,11 +3588,9 @@ importers: publishDirectory: dist packages: + '@0no-co/graphql.web@1.2.0': - resolution: - { - integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==, - } + resolution: {integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==} peerDependencies: graphql: 16.13.0 peerDependenciesMeta: @@ -3567,682 +3598,402 @@ packages: optional: true '@asamuzakjp/css-color@3.2.0': - resolution: - { - integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==, - } + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} '@aws-crypto/crc32@5.2.0': - resolution: - { - integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} '@aws-crypto/crc32c@5.2.0': - resolution: - { - integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==, - } + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} '@aws-crypto/sha1-browser@5.2.0': - resolution: - { - integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==, - } + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} '@aws-crypto/sha256-browser@5.2.0': - resolution: - { - integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==, - } + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} '@aws-crypto/sha256-js@5.2.0': - resolution: - { - integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} '@aws-crypto/supports-web-crypto@5.2.0': - resolution: - { - integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==, - } + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} '@aws-crypto/util@5.2.0': - resolution: - { - integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==, - } + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} '@aws-sdk/client-s3@3.1052.0': - resolution: - { - integrity: sha512-8fgQHfk1WjGUyowyqtMwq9HzZvIQQ86cqn9IZW5Qkq8kaolVjMmZez60qVYxKYvKhVRYUP5hWYPVCyraoud0AA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-8fgQHfk1WjGUyowyqtMwq9HzZvIQQ86cqn9IZW5Qkq8kaolVjMmZez60qVYxKYvKhVRYUP5hWYPVCyraoud0AA==} + engines: {node: '>=20.0.0'} '@aws-sdk/core@3.974.13': - resolution: - { - integrity: sha512-+Y5/4tHki0uYgyx8eun146DegRVQBpdKGK5RbV0FTKJPpaKTchvqVxrrRFK6Wk0JksO4iAZKw3eqxGEIwtO98w==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-+Y5/4tHki0uYgyx8eun146DegRVQBpdKGK5RbV0FTKJPpaKTchvqVxrrRFK6Wk0JksO4iAZKw3eqxGEIwtO98w==} + engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.972.9': - resolution: - { - integrity: sha512-P+QGozmXn2mZZI7sDgk+aUm+RTI61MPSFB+Ir2vjEjEbEsE4e7hYtzrDvAUxZy9ko81h53e11+F/GYlvwDkaOQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-P+QGozmXn2mZZI7sDgk+aUm+RTI61MPSFB+Ir2vjEjEbEsE4e7hYtzrDvAUxZy9ko81h53e11+F/GYlvwDkaOQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-env@3.972.39': - resolution: - { - integrity: sha512-29wX9zpAvEt1vcj0psha+y6ygBHy2V/S72mp6e7q0KARLWXq+pwE/lR6qGkwknQvruh52lXvlqZIga8Hdxkucw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-29wX9zpAvEt1vcj0psha+y6ygBHy2V/S72mp6e7q0KARLWXq+pwE/lR6qGkwknQvruh52lXvlqZIga8Hdxkucw==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-http@3.972.41': - resolution: - { - integrity: sha512-IA3CQTjtJkb6u1H4mE4936c8OPBMa9Jggtwe8U2Mqw/vvb/tZ5Ebd0mcZcX0uKWQhOyYo/+qNIwkV5Xh+FeJJA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-IA3CQTjtJkb6u1H4mE4936c8OPBMa9Jggtwe8U2Mqw/vvb/tZ5Ebd0mcZcX0uKWQhOyYo/+qNIwkV5Xh+FeJJA==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-ini@3.972.43': - resolution: - { - integrity: sha512-4mzII+3mZEVXXE1xzrLQrCJL7/r62A63bA6SVzZoNL5rqCJghpf+xgGltVrIBBs0n+mOZBKrQl2tRREtvZ5l6A==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-4mzII+3mZEVXXE1xzrLQrCJL7/r62A63bA6SVzZoNL5rqCJghpf+xgGltVrIBBs0n+mOZBKrQl2tRREtvZ5l6A==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-login@3.972.43': - resolution: - { - integrity: sha512-HG7kQCwXtbv3oBV61Ins0oNX8KKyvrMqqRkb6ZiAfQHbMuHaiNaEb2KnpKLPkNpqImSBK82UkVE/kaY6IfWikA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-HG7kQCwXtbv3oBV61Ins0oNX8KKyvrMqqRkb6ZiAfQHbMuHaiNaEb2KnpKLPkNpqImSBK82UkVE/kaY6IfWikA==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-node@3.972.44': - resolution: - { - integrity: sha512-sDaBIT0yrNNIPfvlsiTCmANm07zKju+ipWODjEXgZlsjMeIJR3LVp7RDyAOzUoAsTbDfYKDWp+i5WrFiQP6rmQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-sDaBIT0yrNNIPfvlsiTCmANm07zKju+ipWODjEXgZlsjMeIJR3LVp7RDyAOzUoAsTbDfYKDWp+i5WrFiQP6rmQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-process@3.972.39': - resolution: - { - integrity: sha512-2k/amBifLd75eXNwgvPw/2lKYSQ3NhvHQgkVKVjfUq13/eJ3JRtHmznuFenn74OK3sSfp4SMy1YB2w+UVXoKqA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-2k/amBifLd75eXNwgvPw/2lKYSQ3NhvHQgkVKVjfUq13/eJ3JRtHmznuFenn74OK3sSfp4SMy1YB2w+UVXoKqA==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-sso@3.972.43': - resolution: - { - integrity: sha512-LPc3+Y4vhH1T4x6CMqwCM6hk5+SRf/Lwmgm8INm95wxTtIRHcMwQUVkDzWu4Iw/RSncxYM2BC01OrYbxOPZvyg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-LPc3+Y4vhH1T4x6CMqwCM6hk5+SRf/Lwmgm8INm95wxTtIRHcMwQUVkDzWu4Iw/RSncxYM2BC01OrYbxOPZvyg==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-web-identity@3.972.43': - resolution: - { - integrity: sha512-wQtL34lUD/09VXjwAUo2T+I3aEXRDxMB3DKmTJL/Zj0Gi6sLDTrVhae1XVt01yzkquOWajI/sZW72JGDZ1ciTw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-wQtL34lUD/09VXjwAUo2T+I3aEXRDxMB3DKmTJL/Zj0Gi6sLDTrVhae1XVt01yzkquOWajI/sZW72JGDZ1ciTw==} + engines: {node: '>=20.0.0'} '@aws-sdk/lib-storage@3.1052.0': - resolution: - { - integrity: sha512-7agYCtfeOm3ylg95ysiXnmeGKo1rZY5EzDsD9t02hUL8+oo1wO3DuwF2c3IHdez/hB+QSOSKfmk/ZC5/3ybNHQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-7agYCtfeOm3ylg95ysiXnmeGKo1rZY5EzDsD9t02hUL8+oo1wO3DuwF2c3IHdez/hB+QSOSKfmk/ZC5/3ybNHQ==} + engines: {node: '>=20.0.0'} peerDependencies: '@aws-sdk/client-s3': ^3.1052.0 '@aws-sdk/middleware-bucket-endpoint@3.972.15': - resolution: - { - integrity: sha512-O2HDANa+MrvbxpaRVQDiH3T13uAa9AkMjKyZmDygwauAmmvqZ5B0iRmKW+fuVGW6NPXuyXurFgIx69lSvmAWGA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-O2HDANa+MrvbxpaRVQDiH3T13uAa9AkMjKyZmDygwauAmmvqZ5B0iRmKW+fuVGW6NPXuyXurFgIx69lSvmAWGA==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-expect-continue@3.972.13': - resolution: - { - integrity: sha512-sHiqIFg8o2ipT7t40B89Vj0ubSUtY6OSt/+Ee/OXhHch5K4+81zP2+QX8Lkc/nJ2QSmCySxOke7TEbmX69fe2g==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-sHiqIFg8o2ipT7t40B89Vj0ubSUtY6OSt/+Ee/OXhHch5K4+81zP2+QX8Lkc/nJ2QSmCySxOke7TEbmX69fe2g==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-flexible-checksums@3.974.21': - resolution: - { - integrity: sha512-alAu9heyiBK/OmRNXVxq8mmPTgeW2AQ6EYjRsI38kPZa1MZvt2Jh+BlGq7/GG9OVXOaEgD7DlGj/Lzfy5OmuEg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-alAu9heyiBK/OmRNXVxq8mmPTgeW2AQ6EYjRsI38kPZa1MZvt2Jh+BlGq7/GG9OVXOaEgD7DlGj/Lzfy5OmuEg==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-location-constraint@3.972.11': - resolution: - { - integrity: sha512-hkfspNUP4criAH6ton6BGKgnm5dZx+7bUOy1YqlTfejDeUPAM23D81q/IX+hdlS3KUsfwGz5ADTqZWKBEUpf4A==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-hkfspNUP4criAH6ton6BGKgnm5dZx+7bUOy1YqlTfejDeUPAM23D81q/IX+hdlS3KUsfwGz5ADTqZWKBEUpf4A==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-sdk-s3@3.972.42': - resolution: - { - integrity: sha512-/xNqNGXv9LaxZd25L9VV4pnSOw9OdDNO4rAHamM+h3KQBSITljIH9vk3dveGga1I2j36lQd0rdG3gjNEXvtNew==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-/xNqNGXv9LaxZd25L9VV4pnSOw9OdDNO4rAHamM+h3KQBSITljIH9vk3dveGga1I2j36lQd0rdG3gjNEXvtNew==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.11': - resolution: - { - integrity: sha512-7PQvGNhtveKlvVqNahqWx5yrwxP7ecwAoB1dYBf8eKwfo2tzzCbNnW+q2nO3N066ktQaB4iBQbDRWtizm+amoQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-7PQvGNhtveKlvVqNahqWx5yrwxP7ecwAoB1dYBf8eKwfo2tzzCbNnW+q2nO3N066ktQaB4iBQbDRWtizm+amoQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/nested-clients@3.997.11': - resolution: - { - integrity: sha512-nWXXJ1r/r8N2Gw1pWolRgED38/A9A8DHR2ETWIv220zh4PZHcybbR4hUVWWktmNXTRHzDJwRluapHn0rZxuoqA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-nWXXJ1r/r8N2Gw1pWolRgED38/A9A8DHR2ETWIv220zh4PZHcybbR4hUVWWktmNXTRHzDJwRluapHn0rZxuoqA==} + engines: {node: '>=20.0.0'} '@aws-sdk/s3-request-presigner@3.1052.0': - resolution: - { - integrity: sha512-VDULO8AQlgcYZHRYn3qaBfYiM//aWxdlvQsTdcP+EQgSjki3z+mhD7rMy1RKnu4VvRK/xjJFOKwMA3cDM8eLtw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-VDULO8AQlgcYZHRYn3qaBfYiM//aWxdlvQsTdcP+EQgSjki3z+mhD7rMy1RKnu4VvRK/xjJFOKwMA3cDM8eLtw==} + engines: {node: '>=20.0.0'} '@aws-sdk/signature-v4-multi-region@3.996.28': - resolution: - { - integrity: sha512-qs9z5LqXO/CZC2Lg9SGKpoLU8Rhi+m2pFKZqfO9pytX1clc0katqtsDNupJxFy0xT9wsZSPzM2v1y+/H/zfp5Q==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-qs9z5LqXO/CZC2Lg9SGKpoLU8Rhi+m2pFKZqfO9pytX1clc0katqtsDNupJxFy0xT9wsZSPzM2v1y+/H/zfp5Q==} + engines: {node: '>=20.0.0'} '@aws-sdk/token-providers@3.1052.0': - resolution: - { - integrity: sha512-QqZNB3so7UIDxZtroc85TQaLVxdZRFm0eWM1CSR2N+b06as9TOrilvrlTZuj3guYlxMs6yLOgGxnklJ5qMYtTw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-QqZNB3so7UIDxZtroc85TQaLVxdZRFm0eWM1CSR2N+b06as9TOrilvrlTZuj3guYlxMs6yLOgGxnklJ5qMYtTw==} + engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.9': - resolution: - { - integrity: sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==} + engines: {node: '>=20.0.0'} '@aws-sdk/util-locate-window@3.965.5': - resolution: - { - integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/xml-builder@3.972.25': - resolution: - { - integrity: sha512-GH+Kjz4nPKWKHnsiQpnhP1MJdTGIcK4rAka6tzakgjjUkVgNsmPeEbbRAf09SzS1hjGu6duGHCBsxYke0BhHjQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-GH+Kjz4nPKWKHnsiQpnhP1MJdTGIcK4rAka6tzakgjjUkVgNsmPeEbbRAf09SzS1hjGu6duGHCBsxYke0BhHjQ==} + engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.4': - resolution: - { - integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==, - } - engines: { node: '>=18.0.0' } - - '@babel/code-frame@7.27.1': - resolution: - { - integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} '@babel/code-frame@7.28.6': - resolution: - { - integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + engines: {node: '>=6.9.0'} '@babel/code-frame@7.29.0': - resolution: - { - integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} '@babel/compat-data@7.28.6': - resolution: - { - integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} + engines: {node: '>=6.9.0'} '@babel/core@7.28.6': - resolution: - { - integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} + engines: {node: '>=6.9.0'} '@babel/core@7.29.0': - resolution: - { - integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} '@babel/generator@7.29.1': - resolution: - { - integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': - resolution: - { - integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.28.6': - resolution: - { - integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} '@babel/helper-globals@7.28.0': - resolution: - { - integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.28.6': - resolution: - { - integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} '@babel/helper-module-transforms@7.28.6': - resolution: - { - integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-plugin-utils@7.28.6': - resolution: - { - integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.27.1': - resolution: - { - integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@7.28.5': - resolution: - { - integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': - resolution: - { - integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} '@babel/helpers@7.28.6': - resolution: - { - integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} '@babel/helpers@7.29.2': - resolution: - { - integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} '@babel/parser@7.29.0': - resolution: - { - integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} hasBin: true '@babel/parser@7.29.3': - resolution: - { - integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} hasBin: true '@babel/plugin-syntax-async-generators@7.8.4': - resolution: - { - integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, - } + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-bigint@7.8.3': - resolution: - { - integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==, - } + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-properties@7.12.13': - resolution: - { - integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==, - } + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: - { - integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: - { - integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-meta@7.10.4': - resolution: - { - integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==, - } + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-json-strings@7.8.3': - resolution: - { - integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==, - } + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-jsx@7.28.6': - resolution: - { - integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: - { - integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==, - } + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: - { - integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==, - } + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: - { - integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==, - } + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: - { - integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, - } + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: - { - integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==, - } + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: - { - integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==, - } + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: - { - integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: - { - integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-typescript@7.28.6': - resolution: - { - integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: - { - integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: - { - integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/runtime-corejs3@7.28.4': - resolution: - { - integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==} + engines: {node: '>=6.9.0'} '@babel/runtime@7.28.4': - resolution: - { - integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} '@babel/template@7.28.6': - resolution: - { - integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.28.5': - resolution: - { - integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.28.6': - resolution: - { - integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.29.0': - resolution: - { - integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} '@babel/types@7.28.5': - resolution: - { - integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} '@babel/types@7.29.0': - resolution: - { - integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': - resolution: - { - integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, - } + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} '@constructive-io/fetch@1.1.1': - resolution: - { - integrity: sha512-kpus6sqQwMUTBNpqYvSejG27HJXDucV+BQUNUg3T5U92Qy94Q6of7OhbovICteDMmgcRKCdc5S2e2pXnIY8L5A==, - } + resolution: {integrity: sha512-kpus6sqQwMUTBNpqYvSejG27HJXDucV+BQUNUg3T5U92Qy94Q6of7OhbovICteDMmgcRKCdc5S2e2pXnIY8L5A==} '@constructive-io/noble-hashes@0.2.1': - resolution: - { - integrity: sha512-P/C3m4Y9Ywhk7+05aKpg8L7Lkl9Am6AmYMsrMEQ6pRKQA+HIhEutsn9oL99ldsc7gtIt9UEuSHzLRKQvyQCEGQ==, - } + resolution: {integrity: sha512-P/C3m4Y9Ywhk7+05aKpg8L7Lkl9Am6AmYMsrMEQ6pRKQA+HIhEutsn9oL99ldsc7gtIt9UEuSHzLRKQvyQCEGQ==} '@cspotcode/source-map-support@0.8.1': - resolution: - { - integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} '@csstools/color-helpers@5.1.0': - resolution: - { - integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} '@csstools/css-calc@2.1.4': - resolution: - { - integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} peerDependencies: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 '@csstools/css-color-parser@3.1.0': - resolution: - { - integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} peerDependencies: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 '@csstools/css-parser-algorithms@3.0.5': - resolution: - { - integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} peerDependencies: '@csstools/css-tokenizer': ^3.0.4 '@csstools/css-tokenizer@3.0.4': - resolution: - { - integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} '@dataplan/json@1.0.0': - resolution: - { - integrity: sha512-mSBzlhKTZWeXYq/j8U+8/9sVToeVQW4TYfTaEwZvE6fFHJTIzBK38dgOPTN+Vp/Wk7iiRT+GYd8RWE6aMFpNDg==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-mSBzlhKTZWeXYq/j8U+8/9sVToeVQW4TYfTaEwZvE6fFHJTIzBK38dgOPTN+Vp/Wk7iiRT+GYd8RWE6aMFpNDg==} + engines: {node: '>=22'} peerDependencies: grafast: ^1.0.0-rc.8 '@dataplan/pg@1.0.3': - resolution: - { - integrity: sha512-DdgPF+Mg8KntTAC5lW/4w34s74NLCgBgpbw3+PtBhi2QC66acD7noVWZAgzUx/ATouLE9gUiKqHnva89vcNEjA==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-DdgPF+Mg8KntTAC5lW/4w34s74NLCgBgpbw3+PtBhi2QC66acD7noVWZAgzUx/ATouLE9gUiKqHnva89vcNEjA==} + engines: {node: '>=22'} peerDependencies: '@dataplan/json': 1.0.0 grafast: ^1.0.2 @@ -4255,662 +4006,422 @@ packages: optional: true '@emnapi/core@1.10.0': - resolution: - { - integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==, - } + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} '@emnapi/core@1.7.1': - resolution: - { - integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==, - } + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} '@emnapi/runtime@1.10.0': - resolution: - { - integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==, - } + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} '@emnapi/runtime@1.7.1': - resolution: - { - integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==, - } + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': - resolution: - { - integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==, - } + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} '@emnapi/wasi-threads@1.2.1': - resolution: - { - integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==, - } + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@emotion/is-prop-valid@1.4.0': - resolution: - { - integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==, - } + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} '@emotion/memoize@0.9.0': - resolution: - { - integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==, - } + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} '@emotion/stylis@0.8.5': - resolution: - { - integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==, - } + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} '@emotion/unitless@0.7.5': - resolution: - { - integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==, - } + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} '@esbuild/aix-ppc64@0.25.12': - resolution: - { - integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/aix-ppc64@0.27.2': - resolution: - { - integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.25.12': - resolution: - { - integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm64@0.27.2': - resolution: - { - integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm@0.25.12': - resolution: - { - integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-arm@0.27.2': - resolution: - { - integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-x64@0.25.12': - resolution: - { - integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/android-x64@0.27.2': - resolution: - { - integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.25.12': - resolution: - { - integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-arm64@0.27.2': - resolution: - { - integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.25.12': - resolution: - { - integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/darwin-x64@0.27.2': - resolution: - { - integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.25.12': - resolution: - { - integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-arm64@0.27.2': - resolution: - { - integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.25.12': - resolution: - { - integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/freebsd-x64@0.27.2': - resolution: - { - integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.25.12': - resolution: - { - integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm64@0.27.2': - resolution: - { - integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.25.12': - resolution: - { - integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-arm@0.27.2': - resolution: - { - integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.25.12': - resolution: - { - integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-ia32@0.27.2': - resolution: - { - integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.25.12': - resolution: - { - integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-loong64@0.27.2': - resolution: - { - integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.25.12': - resolution: - { - integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-mips64el@0.27.2': - resolution: - { - integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.25.12': - resolution: - { - integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-ppc64@0.27.2': - resolution: - { - integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.25.12': - resolution: - { - integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-riscv64@0.27.2': - resolution: - { - integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.25.12': - resolution: - { - integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-s390x@0.27.2': - resolution: - { - integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.25.12': - resolution: - { - integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/linux-x64@0.27.2': - resolution: - { - integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.25.12': - resolution: - { - integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-arm64@0.27.2': - resolution: - { - integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.25.12': - resolution: - { - integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/netbsd-x64@0.27.2': - resolution: - { - integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.25.12': - resolution: - { - integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-arm64@0.27.2': - resolution: - { - integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.25.12': - resolution: - { - integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openbsd-x64@0.27.2': - resolution: - { - integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openharmony-arm64@0.25.12': - resolution: - { - integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/openharmony-arm64@0.27.2': - resolution: - { - integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/sunos-x64@0.25.12': - resolution: - { - integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/sunos-x64@0.27.2': - resolution: - { - integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.25.12': - resolution: - { - integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-arm64@0.27.2': - resolution: - { - integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.25.12': - resolution: - { - integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-ia32@0.27.2': - resolution: - { - integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.25.12': - resolution: - { - integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} cpu: [x64] os: [win32] '@esbuild/win32-x64@0.27.2': - resolution: - { - integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} cpu: [x64] os: [win32] '@eslint-community/eslint-utils@4.9.0': - resolution: - { - integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/eslint-utils@4.9.1': - resolution: - { - integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/regexpp@4.12.2': - resolution: - { - integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/config-array@0.21.1': - resolution: - { - integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.4.2': - resolution: - { - integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.17.0': - resolution: - { - integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.3': - resolution: - { - integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.39.2': - resolution: - { - integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': - resolution: - { - integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/plugin-kit@0.4.1': - resolution: - { - integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@floating-ui/core@1.7.5': - resolution: - { - integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==, - } + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} '@floating-ui/dom@1.7.6': - resolution: - { - integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==, - } + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} '@floating-ui/react-dom@2.1.8': - resolution: - { - integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==, - } + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' '@floating-ui/react@0.26.28': - resolution: - { - integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==, - } + resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' '@floating-ui/utils@0.2.11': - resolution: - { - integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==, - } + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} '@graphile-contrib/pg-many-to-many@2.0.0-rc.2': - resolution: - { - integrity: sha512-aPu/oPWIsljTmlj58UNy95+JzXwHrClQA51bvfZUgj3l7kaUiwCCBYCFql2nSrMwdlFgexphs3faJbHiqsEDrw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-aPu/oPWIsljTmlj58UNy95+JzXwHrClQA51bvfZUgj3l7kaUiwCCBYCFql2nSrMwdlFgexphs3faJbHiqsEDrw==} + engines: {node: '>=10'} '@graphile/lru@5.0.0': - resolution: - { - integrity: sha512-NeRBDdUd/l4H284HrYL2/wNHv/FmW5stAMPFAiBZanLHwq9J3suZTtyN5CwTxUFA/vgqzu0B1/9XtIEaJYEKig==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-NeRBDdUd/l4H284HrYL2/wNHv/FmW5stAMPFAiBZanLHwq9J3suZTtyN5CwTxUFA/vgqzu0B1/9XtIEaJYEKig==} + engines: {node: '>=22'} '@graphiql/plugin-doc-explorer@0.4.1': - resolution: - { - integrity: sha512-+ram1dDDGMqJn/f9n5I8E6grTvxcM9JZYt/HhtYLuCvkN8kERI6/E3zBHBshhIUnQZoXioZ03fAzXg7JOn0Kyg==, - } + resolution: {integrity: sha512-+ram1dDDGMqJn/f9n5I8E6grTvxcM9JZYt/HhtYLuCvkN8kERI6/E3zBHBshhIUnQZoXioZ03fAzXg7JOn0Kyg==} peerDependencies: '@graphiql/react': ^0.37.0 graphql: 16.13.0 @@ -4919,10 +4430,7 @@ packages: react-dom: ^18 || ^19 '@graphiql/plugin-history@0.4.1': - resolution: - { - integrity: sha512-UyGI/Nm5tzKNMB71li41p6TfkthLqHkmNi9CgHzAM1zKgPIrtSq7Q8WCWKHLOEB5n4/8X8sXFeyQfHgnGYTXYg==, - } + resolution: {integrity: sha512-UyGI/Nm5tzKNMB71li41p6TfkthLqHkmNi9CgHzAM1zKgPIrtSq7Q8WCWKHLOEB5n4/8X8sXFeyQfHgnGYTXYg==} peerDependencies: '@graphiql/react': ^0.37.0 react: ^18 || ^19 @@ -4930,10 +4438,7 @@ packages: react-dom: ^18 || ^19 '@graphiql/react@0.37.3': - resolution: - { - integrity: sha512-rNJjwsYGhcZRdZ2FnyU6ss06xQaZ4UordyvOhp7+b/bEqQiEBpMOLJjuUr48Z6T7zEbZBnzCJpIJyXNqlcfQeA==, - } + resolution: {integrity: sha512-rNJjwsYGhcZRdZ2FnyU6ss06xQaZ4UordyvOhp7+b/bEqQiEBpMOLJjuUr48Z6T7zEbZBnzCJpIJyXNqlcfQeA==} peerDependencies: graphql: 16.13.0 react: ^18 || ^19 @@ -4941,10 +4446,7 @@ packages: react-dom: ^18 || ^19 '@graphiql/toolkit@0.11.3': - resolution: - { - integrity: sha512-Glf0fK1cdHLNq52UWPzfSrYIJuNxy8h4451Pw1ZVpJ7dtU+tm7GVVC64UjEDQ/v2j3fnG4cX8jvR75IvfL6nzQ==, - } + resolution: {integrity: sha512-Glf0fK1cdHLNq52UWPzfSrYIJuNxy8h4451Pw1ZVpJ7dtU+tm7GVVC64UjEDQ/v2j3fnG4cX8jvR75IvfL6nzQ==} peerDependencies: graphql: 16.13.0 graphql-ws: '>= 4.5.0' @@ -4953,64 +4455,40 @@ packages: optional: true '@graphql-typed-document-node/core@3.2.0': - resolution: - { - integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==, - } + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: graphql: 16.13.0 '@headlessui/react@2.2.9': - resolution: - { - integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==} + engines: {node: '>=10'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc '@humanfs/core@0.19.1': - resolution: - { - integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} '@humanfs/node@0.16.7': - resolution: - { - integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': - resolution: - { - integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, - } - engines: { node: '>=12.22' } + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} '@humanwhocodes/retry@0.4.3': - resolution: - { - integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, - } - engines: { node: '>=18.18' } + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} '@hutson/parse-repository-url@3.0.2': - resolution: - { - integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} + engines: {node: '>=6.9.0'} '@inquirer/external-editor@1.0.3': - resolution: - { - integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -5018,10 +4496,7 @@ packages: optional: true '@inquirerer/test@1.4.1': - resolution: - { - integrity: sha512-fDHBK1+0Hfz/Ht6Zhs4Bt89HrBX7KtIayurukbUHIPFZaN4BuWdBxvaBwmrLK1Mq88KIgHA2ZxZ4zdc4AmeEvg==, - } + resolution: {integrity: sha512-fDHBK1+0Hfz/Ht6Zhs4Bt89HrBX7KtIayurukbUHIPFZaN4BuWdBxvaBwmrLK1Mq88KIgHA2ZxZ4zdc4AmeEvg==} peerDependencies: jest: '>=29.0.0' peerDependenciesMeta: @@ -5029,58 +4504,34 @@ packages: optional: true '@inquirerer/utils@3.3.7': - resolution: - { - integrity: sha512-L1M/fwb9VqbYQLorOWw2hgZk2LW4dZ4YsvIj3SJQWNPMmdcDDHDE2wkFK+KT/kh23aGUS7ISM/b0TIiyKeeQQA==, - } + resolution: {integrity: sha512-L1M/fwb9VqbYQLorOWw2hgZk2LW4dZ4YsvIj3SJQWNPMmdcDDHDE2wkFK+KT/kh23aGUS7ISM/b0TIiyKeeQQA==} '@isaacs/cliui@8.0.2': - resolution: - { - integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} '@isaacs/cliui@9.0.0': - resolution: - { - integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} '@isaacs/string-locale-compare@1.1.0': - resolution: - { - integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==, - } + resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} '@istanbuljs/load-nyc-config@1.1.0': - resolution: - { - integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} '@istanbuljs/schema@0.1.6': - resolution: - { - integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} '@jest/console@30.4.1': - resolution: - { - integrity: sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/core@30.4.2': - resolution: - { - integrity: sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -5088,25 +4539,16 @@ packages: optional: true '@jest/diff-sequences@30.0.1': - resolution: - { - integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/diff-sequences@30.4.0': - resolution: - { - integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/environment-jsdom-abstract@30.4.1': - resolution: - { - integrity: sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 jsdom: '*' @@ -5115,74 +4557,44 @@ packages: optional: true '@jest/environment@30.4.1': - resolution: - { - integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect-utils@30.2.0': - resolution: - { - integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect-utils@30.4.1': - resolution: - { - integrity: sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect@30.4.1': - resolution: - { - integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/fake-timers@30.4.1': - resolution: - { - integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/get-type@30.1.0': - resolution: - { - integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/globals@30.4.1': - resolution: - { - integrity: sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/pattern@30.0.1': - resolution: - { - integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/pattern@30.4.0': - resolution: - { - integrity: sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/reporters@30.4.1': - resolution: - { - integrity: sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -5190,872 +4602,527 @@ packages: optional: true '@jest/schemas@29.6.3': - resolution: - { - integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/schemas@30.0.5': - resolution: - { - integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/schemas@30.4.1': - resolution: - { - integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/snapshot-utils@30.4.1': - resolution: - { - integrity: sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/source-map@30.0.1': - resolution: - { - integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-result@30.4.1': - resolution: - { - integrity: sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-sequencer@30.4.1': - resolution: - { - integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/transform@30.4.1': - resolution: - { - integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/types@26.6.2': - resolution: - { - integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==, - } - engines: { node: '>= 10.14.2' } + resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} + engines: {node: '>= 10.14.2'} '@jest/types@30.2.0': - resolution: - { - integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/types@30.4.1': - resolution: - { - integrity: sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jridgewell/gen-mapping@0.3.13': - resolution: - { - integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, - } + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} '@jridgewell/remapping@2.3.5': - resolution: - { - integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, - } + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} '@jridgewell/sourcemap-codec@1.5.5': - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} '@jridgewell/trace-mapping@0.3.31': - resolution: - { - integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, - } + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': - resolution: - { - integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, - } + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} '@launchql/mjml@0.1.1': - resolution: - { - integrity: sha512-6+OEmECuu5atRZ43ovsMfFs+T4NWNaKbzNG0uA8HYaBSn3kWR7GH3QnmL3lCIeymLtvgua8aZChYvg6SxrQdnw==, - } + resolution: {integrity: sha512-6+OEmECuu5atRZ43ovsMfFs+T4NWNaKbzNG0uA8HYaBSn3kWR7GH3QnmL3lCIeymLtvgua8aZChYvg6SxrQdnw==} peerDependencies: react: '>=16' react-dom: '>=16' '@launchql/protobufjs@7.2.6': - resolution: - { - integrity: sha512-vwi1nG2/heVFsIMHQU1KxTjUp5c757CTtRAZn/jutApCkFlle1iv8tzM/DHlSZJKDldxaYqnNYTg0pTyp8Bbtg==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-vwi1nG2/heVFsIMHQU1KxTjUp5c757CTtRAZn/jutApCkFlle1iv8tzM/DHlSZJKDldxaYqnNYTg0pTyp8Bbtg==} + engines: {node: '>=12.0.0'} '@launchql/styled-email@0.1.0': - resolution: - { - integrity: sha512-ISjzsY+3EOH/qAKHPq3evw9QmmEyA8Vw+0pUf+Zf8l4/rAHJJKrSa/uPiaUf2Abi8yAZKyx2uyaZq4ExNNkD+w==, - } + resolution: {integrity: sha512-ISjzsY+3EOH/qAKHPq3evw9QmmEyA8Vw+0pUf+Zf8l4/rAHJJKrSa/uPiaUf2Abi8yAZKyx2uyaZq4ExNNkD+w==} peerDependencies: react: '>=16' react-dom: '>=16' '@lerna/create@8.2.4': - resolution: - { - integrity: sha512-A8AlzetnS2WIuhijdAzKUyFpR5YbLLfV3luQ4lzBgIBgRfuoBDZeF+RSZPhra+7A6/zTUlrbhKZIOi/MNhqgvQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-A8AlzetnS2WIuhijdAzKUyFpR5YbLLfV3luQ4lzBgIBgRfuoBDZeF+RSZPhra+7A6/zTUlrbhKZIOi/MNhqgvQ==} + engines: {node: '>=18.0.0'} deprecated: This package is an implementation detail of Lerna and is no longer published separately. '@n1ru4l/push-pull-async-iterable-iterator@3.2.0': - resolution: - { - integrity: sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==} + engines: {node: '>=12'} '@napi-rs/wasm-runtime@0.2.4': - resolution: - { - integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==, - } + resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} '@napi-rs/wasm-runtime@1.1.4': - resolution: - { - integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==, - } + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 '@noble/hashes@1.8.0': - resolution: - { - integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==, - } - engines: { node: ^14.21.3 || >=16 } + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} '@nodable/entities@2.1.0': - resolution: - { - integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==, - } + resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} '@nodelib/fs.scandir@2.1.5': - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} '@nodelib/fs.stat@2.0.5': - resolution: - { - integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} '@nodelib/fs.walk@1.2.8': - resolution: - { - integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} '@npmcli/agent@2.2.2': - resolution: - { - integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/arborist@7.5.4': - resolution: - { - integrity: sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==} + engines: {node: ^16.14.0 || >=18.0.0} hasBin: true '@npmcli/fs@3.1.1': - resolution: - { - integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/git@5.0.8': - resolution: - { - integrity: sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/installed-package-contents@2.1.0': - resolution: - { - integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true '@npmcli/map-workspaces@3.0.6': - resolution: - { - integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/metavuln-calculator@7.1.1': - resolution: - { - integrity: sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/name-from-folder@2.0.0': - resolution: - { - integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/node-gyp@3.0.0': - resolution: - { - integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/package-json@5.2.0': - resolution: - { - integrity: sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/promise-spawn@7.0.2': - resolution: - { - integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/query@3.1.0': - resolution: - { - integrity: sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/redact@2.0.1': - resolution: - { - integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/run-script@8.1.0': - resolution: - { - integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==} + engines: {node: ^16.14.0 || >=18.0.0} '@nx/devkit@20.8.3': - resolution: - { - integrity: sha512-5lbfJ6ICFOiGeirldQOU5fQ/W/VQ8L3dfWnmHG4UgpWSLoK/YFdRf4lTB4rS0aDXsBL0gyWABz3sZGLPGNYnPA==, - } + resolution: {integrity: sha512-5lbfJ6ICFOiGeirldQOU5fQ/W/VQ8L3dfWnmHG4UgpWSLoK/YFdRf4lTB4rS0aDXsBL0gyWABz3sZGLPGNYnPA==} peerDependencies: nx: '>= 19 <= 21' '@nx/nx-darwin-arm64@20.8.3': - resolution: - { - integrity: sha512-BeYnPAcnaerg6q+qR0bAb0nebwwrsvm4STSVqqVlaqLmmQpU3Bfpx44CEa5d6T9b0V11ZqVE/bkmRhMqhUcrhw==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-BeYnPAcnaerg6q+qR0bAb0nebwwrsvm4STSVqqVlaqLmmQpU3Bfpx44CEa5d6T9b0V11ZqVE/bkmRhMqhUcrhw==} + engines: {node: '>= 10'} cpu: [arm64] os: [darwin] '@nx/nx-darwin-x64@20.8.3': - resolution: - { - integrity: sha512-RIFg1VkQ4jhI+ErqEZuIeGBcJGD8t+u9J5CdQBDIASd8QRhtudBkiYLYCJb+qaQly09G7nVfxuyItlS2uRW3qA==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-RIFg1VkQ4jhI+ErqEZuIeGBcJGD8t+u9J5CdQBDIASd8QRhtudBkiYLYCJb+qaQly09G7nVfxuyItlS2uRW3qA==} + engines: {node: '>= 10'} cpu: [x64] os: [darwin] '@nx/nx-freebsd-x64@20.8.3': - resolution: - { - integrity: sha512-boQTgMUdnqpZhHMrV/xgnp/dTg5dfxw8I4d16NBwmW4j+Sez7zi/dydgsJpfZsj8TicOHvPu6KK4W5wzp82NPw==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-boQTgMUdnqpZhHMrV/xgnp/dTg5dfxw8I4d16NBwmW4j+Sez7zi/dydgsJpfZsj8TicOHvPu6KK4W5wzp82NPw==} + engines: {node: '>= 10'} cpu: [x64] os: [freebsd] '@nx/nx-linux-arm-gnueabihf@20.8.3': - resolution: - { - integrity: sha512-wpiNyY1igx1rLN3EsTLum2lDtblFijdBZB9/9u/6UDub4z9CaQ4yaC4h9n5v7yFYILwfL44YTsQKzrE+iv0y1Q==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-wpiNyY1igx1rLN3EsTLum2lDtblFijdBZB9/9u/6UDub4z9CaQ4yaC4h9n5v7yFYILwfL44YTsQKzrE+iv0y1Q==} + engines: {node: '>= 10'} cpu: [arm] os: [linux] '@nx/nx-linux-arm64-gnu@20.8.3': - resolution: - { - integrity: sha512-nbi/eZtJfWxuDwdUCiP+VJolFubtrz6XxVtB26eMAkODnREOKELHZtMOrlm8JBZCdtWCvTqibq9Az74XsqSfdA==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-nbi/eZtJfWxuDwdUCiP+VJolFubtrz6XxVtB26eMAkODnREOKELHZtMOrlm8JBZCdtWCvTqibq9Az74XsqSfdA==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@nx/nx-linux-arm64-musl@20.8.3': - resolution: - { - integrity: sha512-LTTGzI8YVPlF1v0YlVf+exM+1q7rpsiUbjTTHJcfHFRU5t4BsiZD54K19Y1UBg1XFx5cwhEaIomSmJ88RwPPVQ==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-LTTGzI8YVPlF1v0YlVf+exM+1q7rpsiUbjTTHJcfHFRU5t4BsiZD54K19Y1UBg1XFx5cwhEaIomSmJ88RwPPVQ==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@nx/nx-linux-x64-gnu@20.8.3': - resolution: - { - integrity: sha512-SlA4GtXvQbSzSIWLgiIiLBOjdINPOUR/im+TUbaEMZ8wiGrOY8cnk0PVt95TIQJVBeXBCeb5HnoY0lHJpMOODg==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-SlA4GtXvQbSzSIWLgiIiLBOjdINPOUR/im+TUbaEMZ8wiGrOY8cnk0PVt95TIQJVBeXBCeb5HnoY0lHJpMOODg==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@nx/nx-linux-x64-musl@20.8.3': - resolution: - { - integrity: sha512-MNzkEwPktp5SQH9dJDH2wP9hgG9LsBDhKJXJfKw6sUI/6qz5+/aAjFziKy+zBnhU4AO1yXt5qEWzR8lDcIriVQ==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-MNzkEwPktp5SQH9dJDH2wP9hgG9LsBDhKJXJfKw6sUI/6qz5+/aAjFziKy+zBnhU4AO1yXt5qEWzR8lDcIriVQ==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@nx/nx-win32-arm64-msvc@20.8.3': - resolution: - { - integrity: sha512-qUV7CyXKwRCM/lkvyS6Xa1MqgAuK5da6w27RAehh7LATBUKn1I4/M7DGn6L7ERCxpZuh1TrDz9pUzEy0R+Ekkg==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-qUV7CyXKwRCM/lkvyS6Xa1MqgAuK5da6w27RAehh7LATBUKn1I4/M7DGn6L7ERCxpZuh1TrDz9pUzEy0R+Ekkg==} + engines: {node: '>= 10'} cpu: [arm64] os: [win32] '@nx/nx-win32-x64-msvc@20.8.3': - resolution: - { - integrity: sha512-gX1G8u6W6EPX6PO/wv07+B++UHyCHBXyVWXITA3Kv6HoSajOxIa2Kk1rv1iDQGmX1WWxBaj3bUyYJAFBDITe4w==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-gX1G8u6W6EPX6PO/wv07+B++UHyCHBXyVWXITA3Kv6HoSajOxIa2Kk1rv1iDQGmX1WWxBaj3bUyYJAFBDITe4w==} + engines: {node: '>= 10'} cpu: [x64] os: [win32] '@octokit/auth-token@4.0.0': - resolution: - { - integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} '@octokit/core@5.2.2': - resolution: - { - integrity: sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==} + engines: {node: '>= 18'} '@octokit/endpoint@9.0.6': - resolution: - { - integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==} + engines: {node: '>= 18'} '@octokit/graphql@7.1.1': - resolution: - { - integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==} + engines: {node: '>= 18'} '@octokit/openapi-types@24.2.0': - resolution: - { - integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==, - } + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} '@octokit/plugin-enterprise-rest@6.0.1': - resolution: - { - integrity: sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==, - } + resolution: {integrity: sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==} '@octokit/plugin-paginate-rest@11.4.4-cjs.2': - resolution: - { - integrity: sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==} + engines: {node: '>= 18'} peerDependencies: '@octokit/core': '5' '@octokit/plugin-request-log@4.0.1': - resolution: - { - integrity: sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==} + engines: {node: '>= 18'} peerDependencies: '@octokit/core': '5' '@octokit/plugin-rest-endpoint-methods@13.3.2-cjs.1': - resolution: - { - integrity: sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==} + engines: {node: '>= 18'} peerDependencies: '@octokit/core': ^5 '@octokit/request-error@5.1.1': - resolution: - { - integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==} + engines: {node: '>= 18'} '@octokit/request@8.4.1': - resolution: - { - integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==} + engines: {node: '>= 18'} '@octokit/rest@20.1.2': - resolution: - { - integrity: sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==} + engines: {node: '>= 18'} '@octokit/types@13.10.0': - resolution: - { - integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==, - } + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} '@one-ini/wasm@0.1.1': - resolution: - { - integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==, - } + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} '@oxfmt/binding-android-arm-eabi@0.51.0': - resolution: - { - integrity: sha512-Ni0sCqg5CIHaLIYFGj+ncbcumylvNC6FE4rfD0KfdmnWHbPJ+zev0qZCXKxy2hFVa0fYRK0yPzf5nzPbkZou7g==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-Ni0sCqg5CIHaLIYFGj+ncbcumylvNC6FE4rfD0KfdmnWHbPJ+zev0qZCXKxy2hFVa0fYRK0yPzf5nzPbkZou7g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxfmt/binding-android-arm64@0.51.0': - resolution: - { - integrity: sha512-eu5lAZjuo0KAkp+M24EhDqfOwA8owQ8d7wyBlOUUGRbDLHpU3IRlDHp8Dif+YqGlxs6jra7yS6WQu/NkPhAxeg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-eu5lAZjuo0KAkp+M24EhDqfOwA8owQ8d7wyBlOUUGRbDLHpU3IRlDHp8Dif+YqGlxs6jra7yS6WQu/NkPhAxeg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxfmt/binding-darwin-arm64@0.51.0': - resolution: - { - integrity: sha512-6LsUNIdURhhcIfIn8+xsOb61mSTa9msAHTeSGx9Jf4rsP/gN8PGCF+SKWPAQZbND2w/WBkqQ6303jqEEIXzMdQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-6LsUNIdURhhcIfIn8+xsOb61mSTa9msAHTeSGx9Jf4rsP/gN8PGCF+SKWPAQZbND2w/WBkqQ6303jqEEIXzMdQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxfmt/binding-darwin-x64@0.51.0': - resolution: - { - integrity: sha512-9aUMGmVxdHjYMsEAW1tNRoieTJXlVNDFkRvIR1J7LttJXWjVYCu2ekclLij2KJtxBxSQOYSHd12ME/adVGVbZg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-9aUMGmVxdHjYMsEAW1tNRoieTJXlVNDFkRvIR1J7LttJXWjVYCu2ekclLij2KJtxBxSQOYSHd12ME/adVGVbZg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxfmt/binding-freebsd-x64@0.51.0': - resolution: - { - integrity: sha512-mkY1nhZTqYb+NHaAWxOCKISN6FwdrwMNsu17vTUA3wzUV2VJ+Paq15ZokRcsMU/2PUdHO73prxyeJpjXQ3MPpQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-mkY1nhZTqYb+NHaAWxOCKISN6FwdrwMNsu17vTUA3wzUV2VJ+Paq15ZokRcsMU/2PUdHO73prxyeJpjXQ3MPpQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxfmt/binding-linux-arm-gnueabihf@0.51.0': - resolution: - { - integrity: sha512-wtFwNwE4+YCNuPaWoGDZeGsKvD6D1YSUNBJNn/rJBh7CrDBThFE+TBI5kY7vRW9rIOQRsbW2IpyyL3Du4Zqwiw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-wtFwNwE4+YCNuPaWoGDZeGsKvD6D1YSUNBJNn/rJBh7CrDBThFE+TBI5kY7vRW9rIOQRsbW2IpyyL3Du4Zqwiw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxfmt/binding-linux-arm-musleabihf@0.51.0': - resolution: - { - integrity: sha512-rnOaNx86G7iRKM6lsCIQMux0SMGNC/TEbFR+r7lpruJ12bnrIWgxd5w1PLqOvgR9r8ZJbpK/zfRKctJnh8/Jfg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-rnOaNx86G7iRKM6lsCIQMux0SMGNC/TEbFR+r7lpruJ12bnrIWgxd5w1PLqOvgR9r8ZJbpK/zfRKctJnh8/Jfg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxfmt/binding-linux-arm64-gnu@0.51.0': - resolution: - { - integrity: sha512-jOgDzSqWcICGRjsp4mc08FxKMN8vzP2Kgs4E0d2HUP99F+nJDQKklRV4Zuj+0gcBgjrzx2CbpqaIdUVPepCojA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-jOgDzSqWcICGRjsp4mc08FxKMN8vzP2Kgs4E0d2HUP99F+nJDQKklRV4Zuj+0gcBgjrzx2CbpqaIdUVPepCojA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-arm64-musl@0.51.0': - resolution: - { - integrity: sha512-KBUCdrH5bwVrAvI9gU/1S55oH6fzXjr++J/oVocdu7bYTks1l7DNNT+rLd/1TDdAEjObGwmfWamn7LC1m8A0DQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-KBUCdrH5bwVrAvI9gU/1S55oH6fzXjr++J/oVocdu7bYTks1l7DNNT+rLd/1TDdAEjObGwmfWamn7LC1m8A0DQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxfmt/binding-linux-ppc64-gnu@0.51.0': - resolution: - { - integrity: sha512-NapfjYsABFqTJ1Dn9Efq6sN5esaHconVKwVLbDGNQLrwpOx/g17mkwErHzU72PutL67nf3wNAkbq122H+zLxag==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-NapfjYsABFqTJ1Dn9Efq6sN5esaHconVKwVLbDGNQLrwpOx/g17mkwErHzU72PutL67nf3wNAkbq122H+zLxag==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-riscv64-gnu@0.51.0': - resolution: - { - integrity: sha512-5dlDt1dUZCVi6elIhiK1PWg9wpTzTcIuj0IZnSurvIoMrhOWqqTcc1dSTxcSkNaBZhfsNqRZdINI1zAgbKkJNQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-5dlDt1dUZCVi6elIhiK1PWg9wpTzTcIuj0IZnSurvIoMrhOWqqTcc1dSTxcSkNaBZhfsNqRZdINI1zAgbKkJNQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-riscv64-musl@0.51.0': - resolution: - { - integrity: sha512-pgdWUJn0S5nulyiVdlFV8DzCUnGXkU99W5PSkkmbaZW+LrZBPxpezun4G0DDHbQaVYuJeCuKsXsGKGo77CkUTQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-pgdWUJn0S5nulyiVdlFV8DzCUnGXkU99W5PSkkmbaZW+LrZBPxpezun4G0DDHbQaVYuJeCuKsXsGKGo77CkUTQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxfmt/binding-linux-s390x-gnu@0.51.0': - resolution: - { - integrity: sha512-2XTFUe97CbDGAI8vjwDfZ1HdakO0XIADyJ24idEg64SC4/K4in/OisXVnrW4NMK7I6TgC7EqRhC0Ln/nKhAemA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-2XTFUe97CbDGAI8vjwDfZ1HdakO0XIADyJ24idEg64SC4/K4in/OisXVnrW4NMK7I6TgC7EqRhC0Ln/nKhAemA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-x64-gnu@0.51.0': - resolution: - { - integrity: sha512-kQ1OuCqqt/yyf0ZN9VFxW1/JnlgJgii3Dr7pWf9vNBvrX1hv6g39/+mc5oGRHRGJFZtl3zsGDWR9c5N2B/gwBw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-kQ1OuCqqt/yyf0ZN9VFxW1/JnlgJgii3Dr7pWf9vNBvrX1hv6g39/+mc5oGRHRGJFZtl3zsGDWR9c5N2B/gwBw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-x64-musl@0.51.0': - resolution: - { - integrity: sha512-ARTYqxHF475o96Gbn41hvSWSSRygPlRDXZZgZ9I2scU1y0qiWpCQyZCoefaQa0mwv+wwtZ+luS4YOzsRzM/izg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-ARTYqxHF475o96Gbn41hvSWSSRygPlRDXZZgZ9I2scU1y0qiWpCQyZCoefaQa0mwv+wwtZ+luS4YOzsRzM/izg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxfmt/binding-openharmony-arm64@0.51.0': - resolution: - { - integrity: sha512-QiC1XrCl6a6BmqMzduO8hdIRMf1m44hCkt2Q68KWkTvUB/E7fd2iomyNh6KnnRca5w6eBrRAAtLFqTh+xjsjJA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-QiC1XrCl6a6BmqMzduO8hdIRMf1m44hCkt2Q68KWkTvUB/E7fd2iomyNh6KnnRca5w6eBrRAAtLFqTh+xjsjJA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxfmt/binding-win32-arm64-msvc@0.51.0': - resolution: - { - integrity: sha512-NC/hJb9dtU23Zf8L7IVK95xnFjiQ7AfcLO2l5pb69TDEr958qxrtnB2CveeeNSCBFNIkgaTCfd/vHNSoG78l9g==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-NC/hJb9dtU23Zf8L7IVK95xnFjiQ7AfcLO2l5pb69TDEr958qxrtnB2CveeeNSCBFNIkgaTCfd/vHNSoG78l9g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxfmt/binding-win32-ia32-msvc@0.51.0': - resolution: - { - integrity: sha512-2C45za4Rj36n8YIbhRL1PQbxmXJYf81WEcAgvj5I4ptRROG+A+81hREEN5bmCHADE1UfYaN312U6tkILoZZy6w==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-2C45za4Rj36n8YIbhRL1PQbxmXJYf81WEcAgvj5I4ptRROG+A+81hREEN5bmCHADE1UfYaN312U6tkILoZZy6w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxfmt/binding-win32-x64-msvc@0.51.0': - resolution: - { - integrity: sha512-73RqdAuVKQTkjZIDw08JaDHUM4lav5Qu+CaPwg4QbbA7k8o7LEW0p3UsfZ/F8dsO/pwVYh3RzFcanwLRTTahbQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-73RqdAuVKQTkjZIDw08JaDHUM4lav5Qu+CaPwg4QbbA7k8o7LEW0p3UsfZ/F8dsO/pwVYh3RzFcanwLRTTahbQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@paralleldrive/cuid2@2.3.1': - resolution: - { - integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==, - } + resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} '@pgpm/database-jobs@0.18.0': - resolution: - { - integrity: sha512-3LX7ZQJQEMHd4POG2I2xPzMFR2X6LwvuxJAMpLhcc/I4P3EbEyEig6rPpsyDr9uabRjgUWfZDz05w6cW8X4KwA==, - } + resolution: {integrity: sha512-3LX7ZQJQEMHd4POG2I2xPzMFR2X6LwvuxJAMpLhcc/I4P3EbEyEig6rPpsyDr9uabRjgUWfZDz05w6cW8X4KwA==} '@pgpm/database-jobs@0.21.0': - resolution: - { - integrity: sha512-TN6Uddyq5PJMQ7P+dJOCl0xwyIW1Hv8PkJmRshMNljzt0OVu4Mq8YR5gNkTDveGGHdYCDyP0vhCrpty9aQld6g==, - } + resolution: {integrity: sha512-TN6Uddyq5PJMQ7P+dJOCl0xwyIW1Hv8PkJmRshMNljzt0OVu4Mq8YR5gNkTDveGGHdYCDyP0vhCrpty9aQld6g==} '@pgpm/inflection@0.18.0': - resolution: - { - integrity: sha512-gHfxI6l/+cE9zY5ea6BjVjNcb9VKIavqRXGVyw16Fy0n4HgCmxF8ccZFFPq46JXfakNSk3cPTHDYvvWQaAlIPA==, - } + resolution: {integrity: sha512-gHfxI6l/+cE9zY5ea6BjVjNcb9VKIavqRXGVyw16Fy0n4HgCmxF8ccZFFPq46JXfakNSk3cPTHDYvvWQaAlIPA==} '@pgpm/inflection@0.21.0': - resolution: - { - integrity: sha512-tb1OQCQp1i5SjT04CCN27ptGQcDXidVZrDB8SJsdZZrz6Rcl0WStf4t50z5CSUebg8RJxkpiF4VNpv583RCuhQ==, - } + resolution: {integrity: sha512-tb1OQCQp1i5SjT04CCN27ptGQcDXidVZrDB8SJsdZZrz6Rcl0WStf4t50z5CSUebg8RJxkpiF4VNpv583RCuhQ==} '@pgpm/metaschema-modules@0.21.1': - resolution: - { - integrity: sha512-6RTHN+PcxmTma7k7uHPZdjtN92P3kkqndXxu2acuSOpXi1sxrN5nNSNgi2r42ovl92B8MKy4r7SyF5DUlQwD1A==, - } + resolution: {integrity: sha512-6RTHN+PcxmTma7k7uHPZdjtN92P3kkqndXxu2acuSOpXi1sxrN5nNSNgi2r42ovl92B8MKy4r7SyF5DUlQwD1A==} '@pgpm/metaschema-schema@0.18.0': - resolution: - { - integrity: sha512-joIBoYegI4ZQFFPW7w0HhNXMT+sXnmSytX8YBxGgi5XZqjE2aQdtZcWBIiwZa3MjqU7mYN2xlZt5gPmo/IzLMw==, - } + resolution: {integrity: sha512-joIBoYegI4ZQFFPW7w0HhNXMT+sXnmSytX8YBxGgi5XZqjE2aQdtZcWBIiwZa3MjqU7mYN2xlZt5gPmo/IzLMw==} '@pgpm/metaschema-schema@0.21.0': - resolution: - { - integrity: sha512-kRfVyysGOrGcZEElcAJWaX9Xx2f5fMPtI4rmAxI11vJ0axYUcqtI+9fyp7McwqXnIHr0ShR28jvwqUGaZherJw==, - } + resolution: {integrity: sha512-kRfVyysGOrGcZEElcAJWaX9Xx2f5fMPtI4rmAxI11vJ0axYUcqtI+9fyp7McwqXnIHr0ShR28jvwqUGaZherJw==} '@pgpm/services@0.18.0': - resolution: - { - integrity: sha512-YedLEVdgwkHden55uaZaO8O3HACEiIvblYmPFsQk1ivHwzw1l0KUv6WT5+idwMC/SOsexH/Xq8ckSSM84ErWtQ==, - } + resolution: {integrity: sha512-YedLEVdgwkHden55uaZaO8O3HACEiIvblYmPFsQk1ivHwzw1l0KUv6WT5+idwMC/SOsexH/Xq8ckSSM84ErWtQ==} '@pgpm/types@0.18.0': - resolution: - { - integrity: sha512-w6pfcS5HuJ2IGfyn74mGChl0nhNBIbxdu6sarlARGveZFHvBkTIPKEezA524sLKhUHda84YWuExEhCUd6N/AqQ==, - } + resolution: {integrity: sha512-w6pfcS5HuJ2IGfyn74mGChl0nhNBIbxdu6sarlARGveZFHvBkTIPKEezA524sLKhUHda84YWuExEhCUd6N/AqQ==} '@pgpm/types@0.21.0': - resolution: - { - integrity: sha512-F5ph4z7JWwKVcy/CmAgodEIXqXCwSgv0ESVxIV2spdl01RgNi0SdKuHqVJnjp8rbHHUtlCPRj/UlAopUSpIIOg==, - } + resolution: {integrity: sha512-F5ph4z7JWwKVcy/CmAgodEIXqXCwSgv0ESVxIV2spdl01RgNi0SdKuHqVJnjp8rbHHUtlCPRj/UlAopUSpIIOg==} '@pgpm/verify@0.18.0': - resolution: - { - integrity: sha512-7XtY+hj9CbYb0ZhD0LJRp+TErtYS2z0FZbdkMNM7UabEwz05VLlS/lXbdtn5hksbUq6dkIxwxQ2mfFpATPpqDQ==, - } + resolution: {integrity: sha512-7XtY+hj9CbYb0ZhD0LJRp+TErtYS2z0FZbdkMNM7UabEwz05VLlS/lXbdtn5hksbUq6dkIxwxQ2mfFpATPpqDQ==} '@pgpm/verify@0.21.0': - resolution: - { - integrity: sha512-z4iiwEUrUR+aIvxfu4+HefitVPLtypXdbyEkbNJ//EMsnzturo6wzxT1e60w/Xp29AzF6m5OaJ0djc6lzr9hTw==, - } + resolution: {integrity: sha512-z4iiwEUrUR+aIvxfu4+HefitVPLtypXdbyEkbNJ//EMsnzturo6wzxT1e60w/Xp29AzF6m5OaJ0djc6lzr9hTw==} '@pgsql/quotes@17.1.0': - resolution: - { - integrity: sha512-J/H+LcrENBpYgL45WW6aTjb5Yk4tX4+AmB2/k8KZa+Zh3wiCtqmNIag+HZz5HmWaF6EZK9ZGC95NBD1fs+rUvg==, - } + resolution: {integrity: sha512-J/H+LcrENBpYgL45WW6aTjb5Yk4tX4+AmB2/k8KZa+Zh3wiCtqmNIag+HZz5HmWaF6EZK9ZGC95NBD1fs+rUvg==} '@pgsql/traverse@17.2.6': - resolution: - { - integrity: sha512-BLOE9DUcvd3y3Ogf56mmpTONPylnMuFCo9PvHQA9SXavcRPhRtvIZ/sRO2ja+bUWK/3KTLJ1Hb61CbbdPkcHoA==, - } + resolution: {integrity: sha512-BLOE9DUcvd3y3Ogf56mmpTONPylnMuFCo9PvHQA9SXavcRPhRtvIZ/sRO2ja+bUWK/3KTLJ1Hb61CbbdPkcHoA==} '@pgsql/types@17.6.2': - resolution: - { - integrity: sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==, - } + resolution: {integrity: sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==} '@pgsql/utils@17.8.17': - resolution: - { - integrity: sha512-JAbRaKBdH0c6GbSgiepwdsis9AG9Kgz9sdmL5e+iBYFZRsFBGKoe1jnxJ0tAMjWYcLus2qeDN6gqQaPYtx1EOg==, - } + resolution: {integrity: sha512-JAbRaKBdH0c6GbSgiepwdsis9AG9Kgz9sdmL5e+iBYFZRsFBGKoe1jnxJ0tAMjWYcLus2qeDN6gqQaPYtx1EOg==} '@pkgjs/parseargs@0.11.0': - resolution: - { - integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} '@pkgr/core@0.2.9': - resolution: - { - integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==, - } - engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} '@playwright/test@1.60.0': - resolution: - { - integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} + engines: {node: '>=18'} hasBin: true '@protobufjs/aspromise@1.1.2': - resolution: - { - integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==, - } + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} '@protobufjs/base64@1.1.2': - resolution: - { - integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==, - } + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} '@protobufjs/codegen@2.0.5': - resolution: - { - integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==, - } + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} '@protobufjs/eventemitter@1.1.0': - resolution: - { - integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==, - } + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} '@protobufjs/fetch@1.1.0': - resolution: - { - integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==, - } + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} '@protobufjs/float@1.0.2': - resolution: - { - integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==, - } + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} '@protobufjs/inquire@1.1.1': - resolution: - { - integrity: sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==, - } + resolution: {integrity: sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==} '@protobufjs/path@1.1.2': - resolution: - { - integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==, - } + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} '@protobufjs/pool@1.1.0': - resolution: - { - integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==, - } + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} '@protobufjs/utf8@1.1.1': - resolution: - { - integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==, - } + resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} '@radix-ui/primitive@1.1.3': - resolution: - { - integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==, - } + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} '@radix-ui/react-arrow@1.1.7': - resolution: - { - integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==, - } + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6068,10 +5135,7 @@ packages: optional: true '@radix-ui/react-collection@1.1.7': - resolution: - { - integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==, - } + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6084,10 +5148,7 @@ packages: optional: true '@radix-ui/react-compose-refs@1.1.2': - resolution: - { - integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==, - } + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6096,10 +5157,7 @@ packages: optional: true '@radix-ui/react-context@1.1.2': - resolution: - { - integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==, - } + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6108,10 +5166,7 @@ packages: optional: true '@radix-ui/react-dialog@1.1.15': - resolution: - { - integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==, - } + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6124,10 +5179,7 @@ packages: optional: true '@radix-ui/react-direction@1.1.1': - resolution: - { - integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==, - } + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6136,10 +5188,7 @@ packages: optional: true '@radix-ui/react-dismissable-layer@1.1.11': - resolution: - { - integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==, - } + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6152,10 +5201,7 @@ packages: optional: true '@radix-ui/react-dropdown-menu@2.1.16': - resolution: - { - integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==, - } + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6168,10 +5214,7 @@ packages: optional: true '@radix-ui/react-focus-guards@1.1.3': - resolution: - { - integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==, - } + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6180,10 +5223,7 @@ packages: optional: true '@radix-ui/react-focus-scope@1.1.7': - resolution: - { - integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==, - } + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6196,10 +5236,7 @@ packages: optional: true '@radix-ui/react-id@1.1.1': - resolution: - { - integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==, - } + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6208,10 +5245,7 @@ packages: optional: true '@radix-ui/react-menu@2.1.16': - resolution: - { - integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==, - } + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6224,10 +5258,7 @@ packages: optional: true '@radix-ui/react-popper@1.2.8': - resolution: - { - integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==, - } + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6240,10 +5271,7 @@ packages: optional: true '@radix-ui/react-portal@1.1.9': - resolution: - { - integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==, - } + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6256,10 +5284,7 @@ packages: optional: true '@radix-ui/react-presence@1.1.5': - resolution: - { - integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==, - } + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6272,10 +5297,7 @@ packages: optional: true '@radix-ui/react-primitive@2.1.3': - resolution: - { - integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==, - } + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6288,10 +5310,7 @@ packages: optional: true '@radix-ui/react-primitive@2.1.4': - resolution: - { - integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==, - } + resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6304,10 +5323,7 @@ packages: optional: true '@radix-ui/react-roving-focus@1.1.11': - resolution: - { - integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==, - } + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6320,10 +5336,7 @@ packages: optional: true '@radix-ui/react-slot@1.2.3': - resolution: - { - integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==, - } + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6332,10 +5345,7 @@ packages: optional: true '@radix-ui/react-slot@1.2.4': - resolution: - { - integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==, - } + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6344,10 +5354,7 @@ packages: optional: true '@radix-ui/react-tooltip@1.2.8': - resolution: - { - integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==, - } + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6360,10 +5367,7 @@ packages: optional: true '@radix-ui/react-use-callback-ref@1.1.1': - resolution: - { - integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==, - } + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6372,10 +5376,7 @@ packages: optional: true '@radix-ui/react-use-controllable-state@1.2.2': - resolution: - { - integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==, - } + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6384,10 +5385,7 @@ packages: optional: true '@radix-ui/react-use-effect-event@0.0.2': - resolution: - { - integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==, - } + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6396,10 +5394,7 @@ packages: optional: true '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: - { - integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==, - } + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6408,10 +5403,7 @@ packages: optional: true '@radix-ui/react-use-layout-effect@1.1.1': - resolution: - { - integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==, - } + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6420,10 +5412,7 @@ packages: optional: true '@radix-ui/react-use-rect@1.1.1': - resolution: - { - integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==, - } + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6432,10 +5421,7 @@ packages: optional: true '@radix-ui/react-use-size@1.1.1': - resolution: - { - integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==, - } + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -6444,10 +5430,7 @@ packages: optional: true '@radix-ui/react-visually-hidden@1.2.3': - resolution: - { - integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==, - } + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6460,10 +5443,7 @@ packages: optional: true '@radix-ui/react-visually-hidden@1.2.4': - resolution: - { - integrity: sha512-kaeiyGCe844dkb9AVF+rb4yTyb1LiLN/e3es3nLiRyN4dC8AduBYPMnnNlDjX2VDOcvDEiPnRNMJeWCfsX0txg==, - } + resolution: {integrity: sha512-kaeiyGCe844dkb9AVF+rb4yTyb1LiLN/e3es3nLiRyN4dC8AduBYPMnnNlDjX2VDOcvDEiPnRNMJeWCfsX0txg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -6476,581 +5456,354 @@ packages: optional: true '@radix-ui/rect@1.1.1': - resolution: - { - integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==, - } + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} '@react-aria/focus@3.21.5': - resolution: - { - integrity: sha512-V18fwCyf8zqgJdpLQeDU5ZRNd9TeOfBbhLgmX77Zr5ae9XwaoJ1R3SFJG1wCJX60t34AW+aLZSEEK+saQElf3Q==, - } + resolution: {integrity: sha512-V18fwCyf8zqgJdpLQeDU5ZRNd9TeOfBbhLgmX77Zr5ae9XwaoJ1R3SFJG1wCJX60t34AW+aLZSEEK+saQElf3Q==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@react-aria/interactions@3.27.1': - resolution: - { - integrity: sha512-M3wLpTTmDflI0QGNK0PJNUaBXXfeBXue8ZxLMngfc1piHNiH4G5lUvWd9W14XVbqrSCVY8i8DfGrNYpyyZu0tw==, - } + resolution: {integrity: sha512-M3wLpTTmDflI0QGNK0PJNUaBXXfeBXue8ZxLMngfc1piHNiH4G5lUvWd9W14XVbqrSCVY8i8DfGrNYpyyZu0tw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@react-aria/ssr@3.9.10': - resolution: - { - integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==, - } - engines: { node: '>= 12' } + resolution: {integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==} + engines: {node: '>= 12'} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@react-aria/utils@3.33.1': - resolution: - { - integrity: sha512-kIx1Sj6bbAT0pdqCegHuPanR9zrLn5zMRiM7LN12rgRf55S19ptd9g3ncahArifYTRkfEU9VIn+q0HjfMqS9/w==, - } + resolution: {integrity: sha512-kIx1Sj6bbAT0pdqCegHuPanR9zrLn5zMRiM7LN12rgRf55S19ptd9g3ncahArifYTRkfEU9VIn+q0HjfMqS9/w==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@react-stately/flags@3.1.2': - resolution: - { - integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==, - } + resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} '@react-stately/utils@3.11.0': - resolution: - { - integrity: sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==, - } + resolution: {integrity: sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@react-types/shared@3.33.1': - resolution: - { - integrity: sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag==, - } + resolution: {integrity: sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@rolldown/pluginutils@1.0.0-beta.27': - resolution: - { - integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==, - } + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} '@rollup/rollup-android-arm-eabi@4.57.1': - resolution: - { - integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==, - } + resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.57.1': - resolution: - { - integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==, - } + resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} cpu: [arm64] os: [android] '@rollup/rollup-darwin-arm64@4.57.1': - resolution: - { - integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==, - } + resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.57.1': - resolution: - { - integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==, - } + resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} cpu: [x64] os: [darwin] '@rollup/rollup-freebsd-arm64@4.57.1': - resolution: - { - integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==, - } + resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.57.1': - resolution: - { - integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==, - } + resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} cpu: [x64] os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.57.1': - resolution: - { - integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==, - } + resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.57.1': - resolution: - { - integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==, - } + resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.57.1': - resolution: - { - integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==, - } + resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.57.1': - resolution: - { - integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==, - } + resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.57.1': - resolution: - { - integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==, - } + resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.57.1': - resolution: - { - integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==, - } + resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.57.1': - resolution: - { - integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==, - } + resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.57.1': - resolution: - { - integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==, - } + resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.57.1': - resolution: - { - integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==, - } + resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.57.1': - resolution: - { - integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==, - } + resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.57.1': - resolution: - { - integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==, - } + resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.57.1': - resolution: - { - integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==, - } + resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.57.1': - resolution: - { - integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==, - } + resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.57.1': - resolution: - { - integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==, - } + resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} cpu: [x64] os: [openbsd] '@rollup/rollup-openharmony-arm64@4.57.1': - resolution: - { - integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==, - } + resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} cpu: [arm64] os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.57.1': - resolution: - { - integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==, - } + resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.57.1': - resolution: - { - integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==, - } + resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-gnu@4.57.1': - resolution: - { - integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==, - } + resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.57.1': - resolution: - { - integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==, - } + resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} cpu: [x64] os: [win32] '@sigstore/bundle@2.3.2': - resolution: - { - integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/core@1.1.0': - resolution: - { - integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/protobuf-specs@0.3.3': - resolution: - { - integrity: sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==, - } - engines: { node: ^18.17.0 || >=20.5.0 } + resolution: {integrity: sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==} + engines: {node: ^18.17.0 || >=20.5.0} '@sigstore/sign@2.3.2': - resolution: - { - integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/tuf@2.3.4': - resolution: - { - integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/verify@1.2.1': - resolution: - { - integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==} + engines: {node: ^16.14.0 || >=18.0.0} '@sinclair/typebox@0.27.8': - resolution: - { - integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==, - } + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} '@sinclair/typebox@0.34.47': - resolution: - { - integrity: sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==, - } + resolution: {integrity: sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==} '@sinclair/typebox@0.34.49': - resolution: - { - integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==, - } + resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} '@sinonjs/commons@3.0.1': - resolution: - { - integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==, - } + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} '@sinonjs/fake-timers@15.4.0': - resolution: - { - integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==, - } + resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} '@smithy/abort-controller@4.2.15': - resolution: - { - integrity: sha512-98zuTrdp06d5jhoIA7gBzEAh+4zZMywxdS1qiiUg0BOTq/EE4zc0bqgbSBKMcRKSNFZrpVFjspExiP7ScO2l5A==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-98zuTrdp06d5jhoIA7gBzEAh+4zZMywxdS1qiiUg0BOTq/EE4zc0bqgbSBKMcRKSNFZrpVFjspExiP7ScO2l5A==} + engines: {node: '>=18.0.0'} '@smithy/core@3.24.4': - resolution: - { - integrity: sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==} + engines: {node: '>=18.0.0'} '@smithy/credential-provider-imds@4.3.4': - resolution: - { - integrity: sha512-vKW0MEFRU4Y3MkVZUkpJm+g9qyPGLCXhc0YLggUdSdBB4g7IaSSsCE75P9rBXyWHrXY1UYSQUl8/DwsTR7QciA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-vKW0MEFRU4Y3MkVZUkpJm+g9qyPGLCXhc0YLggUdSdBB4g7IaSSsCE75P9rBXyWHrXY1UYSQUl8/DwsTR7QciA==} + engines: {node: '>=18.0.0'} '@smithy/fetch-http-handler@5.4.4': - resolution: - { - integrity: sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ==} + engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': - resolution: - { - integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} '@smithy/node-http-handler@4.4.16': - resolution: - { - integrity: sha512-ULC8UCS/HivdCB3jhi+kLFYe4B5gxH2gi9vHBfEIiRrT2jfKiZNiETJSlzRtE6B26XbBHjPtc8iZKSNqMol9bw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-ULC8UCS/HivdCB3jhi+kLFYe4B5gxH2gi9vHBfEIiRrT2jfKiZNiETJSlzRtE6B26XbBHjPtc8iZKSNqMol9bw==} + engines: {node: '>=18.0.0'} '@smithy/protocol-http@5.4.4': - resolution: - { - integrity: sha512-5VdJYIYsVt2GT+i0fp5gvWoJNrdFEFN16TrpNnAZHngYC/xgk5yni6O/qV3WlIpJjeLC8RfwoQiNTljCdbNXgw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-5VdJYIYsVt2GT+i0fp5gvWoJNrdFEFN16TrpNnAZHngYC/xgk5yni6O/qV3WlIpJjeLC8RfwoQiNTljCdbNXgw==} + engines: {node: '>=18.0.0'} '@smithy/querystring-builder@4.3.4': - resolution: - { - integrity: sha512-cti+qc0OmNMtot9B2bOPyXfoXBBqcl/XEPGq32hORW0BZKNEK/bDp6xDHm5cFtV+96jj7vIZ17AHgf6ELJ9ZBw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-cti+qc0OmNMtot9B2bOPyXfoXBBqcl/XEPGq32hORW0BZKNEK/bDp6xDHm5cFtV+96jj7vIZ17AHgf6ELJ9ZBw==} + engines: {node: '>=18.0.0'} '@smithy/signature-v4@5.4.4': - resolution: - { - integrity: sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==} + engines: {node: '>=18.0.0'} '@smithy/types@4.14.2': - resolution: - { - integrity: sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==} + engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': - resolution: - { - integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} '@smithy/util-utf8@2.3.0': - resolution: - { - integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} '@styled-system/background@5.1.2': - resolution: - { - integrity: sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==, - } + resolution: {integrity: sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==} '@styled-system/border@5.1.5': - resolution: - { - integrity: sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==, - } + resolution: {integrity: sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==} '@styled-system/color@5.1.2': - resolution: - { - integrity: sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==, - } + resolution: {integrity: sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==} '@styled-system/core@5.1.2': - resolution: - { - integrity: sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==, - } + resolution: {integrity: sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==} '@styled-system/css@5.1.5': - resolution: - { - integrity: sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==, - } + resolution: {integrity: sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==} '@styled-system/flexbox@5.1.2': - resolution: - { - integrity: sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==, - } + resolution: {integrity: sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==} '@styled-system/grid@5.1.2': - resolution: - { - integrity: sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==, - } + resolution: {integrity: sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==} '@styled-system/layout@5.1.2': - resolution: - { - integrity: sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==, - } + resolution: {integrity: sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==} '@styled-system/position@5.1.2': - resolution: - { - integrity: sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==, - } + resolution: {integrity: sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==} '@styled-system/shadow@5.1.2': - resolution: - { - integrity: sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==, - } + resolution: {integrity: sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==} '@styled-system/space@5.1.2': - resolution: - { - integrity: sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==, - } + resolution: {integrity: sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==} '@styled-system/typography@5.1.2': - resolution: - { - integrity: sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==, - } + resolution: {integrity: sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==} '@styled-system/variant@5.1.5': - resolution: - { - integrity: sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==, - } + resolution: {integrity: sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==} '@swc/helpers@0.5.19': - resolution: - { - integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==, - } + resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} '@tanstack/query-core@5.90.20': - resolution: - { - integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==, - } + resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} '@tanstack/react-query@5.90.21': - resolution: - { - integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==, - } + resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==} peerDependencies: react: ^18 || ^19 '@tanstack/react-virtual@3.13.22': - resolution: - { - integrity: sha512-EaOrBBJLi3M0bTMQRjGkxLXRw7Gizwntoy5E2Q2UnSbML7Mo2a1P/Hfkw5tw9FLzK62bj34Jl6VNbQfRV6eJcA==, - } + resolution: {integrity: sha512-EaOrBBJLi3M0bTMQRjGkxLXRw7Gizwntoy5E2Q2UnSbML7Mo2a1P/Hfkw5tw9FLzK62bj34Jl6VNbQfRV6eJcA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 '@tanstack/virtual-core@3.13.22': - resolution: - { - integrity: sha512-isuUGKsc5TAPDoHSbWTbl1SCil54zOS2MiWz/9GCWHPUQOvNTQx8qJEWC7UWR0lShhbK0Lmkcf0SZYxvch7G3g==, - } + resolution: {integrity: sha512-isuUGKsc5TAPDoHSbWTbl1SCil54zOS2MiWz/9GCWHPUQOvNTQx8qJEWC7UWR0lShhbK0Lmkcf0SZYxvch7G3g==} '@testing-library/dom@10.4.1': - resolution: - { - integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} '@testing-library/dom@7.31.2': - resolution: - { - integrity: sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==} + engines: {node: '>=10'} '@testing-library/jest-dom@5.11.10': - resolution: - { - integrity: sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ==, - } - engines: { node: '>=8', npm: '>=6', yarn: '>=1' } + resolution: {integrity: sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ==} + engines: {node: '>=8', npm: '>=6', yarn: '>=1'} '@testing-library/react@11.2.5': - resolution: - { - integrity: sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ==} + engines: {node: '>=10'} peerDependencies: react: '*' react-dom: '*' '@testing-library/react@16.3.2': - resolution: - { - integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 '@types/react': ^18.0.0 || ^19.0.0 @@ -7064,1789 +5817,1004 @@ packages: optional: true '@tsconfig/node10@1.0.12': - resolution: - { - integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==, - } + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} '@tsconfig/node12@1.0.11': - resolution: - { - integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==, - } + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} '@tsconfig/node14@1.0.3': - resolution: - { - integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==, - } + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} '@tsconfig/node16@1.0.4': - resolution: - { - integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==, - } + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} '@tufjs/canonical-json@2.0.0': - resolution: - { - integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} + engines: {node: ^16.14.0 || >=18.0.0} '@tufjs/models@2.0.1': - resolution: - { - integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==} + engines: {node: ^16.14.0 || >=18.0.0} '@tybys/wasm-util@0.10.2': - resolution: - { - integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==, - } + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} '@tybys/wasm-util@0.9.0': - resolution: - { - integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==, - } + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} '@types/accept-language-parser@1.5.8': - resolution: - { - integrity: sha512-6+dKdh9q/I8xDBnKQKddCBKaWBWLmJ97HTiSbAXVpL7LEgDfOkKF98UVCaZ5KJrtdN5Wa5ndXUiqD3XR9XGqWQ==, - } + resolution: {integrity: sha512-6+dKdh9q/I8xDBnKQKddCBKaWBWLmJ97HTiSbAXVpL7LEgDfOkKF98UVCaZ5KJrtdN5Wa5ndXUiqD3XR9XGqWQ==} '@types/accepts@1.3.7': - resolution: - { - integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==, - } + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} '@types/aria-query@4.2.2': - resolution: - { - integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==, - } + resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} '@types/aria-query@5.0.4': - resolution: - { - integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, - } + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} '@types/babel__core@7.20.5': - resolution: - { - integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, - } + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} '@types/babel__generator@7.27.0': - resolution: - { - integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, - } + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': - resolution: - { - integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, - } + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} '@types/babel__traverse@7.28.0': - resolution: - { - integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==, - } + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} '@types/body-parser@1.19.6': - resolution: - { - integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==, - } + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} '@types/connect@3.4.38': - resolution: - { - integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==, - } + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} '@types/content-disposition@0.5.9': - resolution: - { - integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==, - } + resolution: {integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==} '@types/cookie-parser@1.4.10': - resolution: - { - integrity: sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==, - } + resolution: {integrity: sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==} peerDependencies: '@types/express': '*' '@types/cookiejar@2.1.5': - resolution: - { - integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==, - } + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} '@types/cookies@0.9.2': - resolution: - { - integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==, - } + resolution: {integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==} '@types/cors@2.8.19': - resolution: - { - integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==, - } + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} '@types/estree@1.0.8': - resolution: - { - integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, - } + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/express-serve-static-core@5.1.0': - resolution: - { - integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==, - } + resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} '@types/express@5.0.6': - resolution: - { - integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==, - } + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} '@types/geojson@7946.0.16': - resolution: - { - integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==, - } + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} '@types/graphql-upload@8.0.12': - resolution: - { - integrity: sha512-M0ZPZqNUzKNB16q5woEzgG/Q8DjICV80K7JvDSRnDmDFfrRdfFX/n6PbmqAN7gCzECcHVnw1gk6N4Cg0FwxCqA==, - } + resolution: {integrity: sha512-M0ZPZqNUzKNB16q5woEzgG/Q8DjICV80K7JvDSRnDmDFfrRdfFX/n6PbmqAN7gCzECcHVnw1gk6N4Cg0FwxCqA==} '@types/http-assert@1.5.6': - resolution: - { - integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==, - } + resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} '@types/http-errors@2.0.5': - resolution: - { - integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==, - } + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} '@types/istanbul-lib-coverage@2.0.6': - resolution: - { - integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==, - } + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} '@types/istanbul-lib-report@3.0.3': - resolution: - { - integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==, - } + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} '@types/istanbul-reports@3.0.4': - resolution: - { - integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==, - } + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} '@types/jest-in-case@1.0.9': - resolution: - { - integrity: sha512-tapHpzWGjCC/hxYJyzbJ/5ZV6rA2153Sve5lGJUAIA1Jzrphfp27TznAWfGeXf+d8TLN7zMujaC0UwNQwSJaQg==, - } + resolution: {integrity: sha512-tapHpzWGjCC/hxYJyzbJ/5ZV6rA2153Sve5lGJUAIA1Jzrphfp27TznAWfGeXf+d8TLN7zMujaC0UwNQwSJaQg==} '@types/jest@30.0.0': - resolution: - { - integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==, - } + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} '@types/js-yaml@4.0.9': - resolution: - { - integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==, - } + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} '@types/jsdom@21.1.7': - resolution: - { - integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==, - } + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} '@types/json-schema@7.0.15': - resolution: - { - integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, - } + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/keygrip@1.0.6': - resolution: - { - integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==, - } + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} '@types/koa-compose@3.2.9': - resolution: - { - integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==, - } + resolution: {integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==} '@types/koa@3.0.1': - resolution: - { - integrity: sha512-VkB6WJUQSe0zBpR+Q7/YIUESGp5wPHcaXr0xueU5W0EOUWtlSbblsl+Kl31lyRQ63nIILh0e/7gXjQ09JXJIHw==, - } + resolution: {integrity: sha512-VkB6WJUQSe0zBpR+Q7/YIUESGp5wPHcaXr0xueU5W0EOUWtlSbblsl+Kl31lyRQ63nIILh0e/7gXjQ09JXJIHw==} '@types/methods@1.1.4': - resolution: - { - integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==, - } + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} '@types/minimatch@3.0.5': - resolution: - { - integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==, - } + resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} '@types/minimist@1.2.5': - resolution: - { - integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==, - } + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} '@types/node@22.19.11': - resolution: - { - integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==, - } + resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} '@types/node@22.19.15': - resolution: - { - integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==, - } + resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==} '@types/node@22.19.19': - resolution: - { - integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==, - } + resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} '@types/node@25.9.1': - resolution: - { - integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==, - } + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} '@types/nodemailer@7.0.11': - resolution: - { - integrity: sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==, - } + resolution: {integrity: sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==} '@types/normalize-package-data@2.4.4': - resolution: - { - integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==, - } + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} '@types/pg-copy-streams@1.2.5': - resolution: - { - integrity: sha512-7D6/GYW2uHIaVU6S/5omI+6RZnwlZBpLQDZAH83xX1rjxAOK0f6/deKyyUTewxqts145VIGn6XWYz1YGf50G5g==, - } + resolution: {integrity: sha512-7D6/GYW2uHIaVU6S/5omI+6RZnwlZBpLQDZAH83xX1rjxAOK0f6/deKyyUTewxqts145VIGn6XWYz1YGf50G5g==} '@types/pg@8.20.0': - resolution: - { - integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==, - } + resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==} '@types/pluralize@0.0.33': - resolution: - { - integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==, - } + resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==} '@types/qs@6.14.0': - resolution: - { - integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==, - } + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} '@types/range-parser@1.2.7': - resolution: - { - integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==, - } + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} '@types/react-dom@19.2.3': - resolution: - { - integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==, - } + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 '@types/react@19.2.14': - resolution: - { - integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==, - } + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} '@types/react@19.2.15': - resolution: - { - integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==, - } + resolution: {integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==} '@types/request-ip@0.0.41': - resolution: - { - integrity: sha512-Qzz0PM2nSZej4lsLzzNfADIORZhhxO7PED0fXpg4FjXiHuJ/lMyUg+YFF5q8x9HPZH3Gl6N+NOM8QZjItNgGKg==, - } + resolution: {integrity: sha512-Qzz0PM2nSZej4lsLzzNfADIORZhhxO7PED0fXpg4FjXiHuJ/lMyUg+YFF5q8x9HPZH3Gl6N+NOM8QZjItNgGKg==} '@types/semver@7.7.1': - resolution: - { - integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==, - } + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} '@types/send@1.2.1': - resolution: - { - integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==, - } + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} '@types/serve-static@2.2.0': - resolution: - { - integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==, - } + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} '@types/shelljs@0.10.0': - resolution: - { - integrity: sha512-OEfyhE5Ox+FeoHbhrEDwm0kXxntO6nsyMRCFvNsIBHPZu5rV1w2OjPcLclaC/IZ1TlzZPgbeMfwAZEi5N238yQ==, - } + resolution: {integrity: sha512-OEfyhE5Ox+FeoHbhrEDwm0kXxntO6nsyMRCFvNsIBHPZu5rV1w2OjPcLclaC/IZ1TlzZPgbeMfwAZEi5N238yQ==} '@types/smtp-server@3.5.13': - resolution: - { - integrity: sha512-S3rGl2KbViH+98/CgHipPIWgtAFnjxLsppxIRbgJHNuZL7Y+py+7kZjT7xS+wOmCxYTn4O7IqLqkvekg99MDVQ==, - } + resolution: {integrity: sha512-S3rGl2KbViH+98/CgHipPIWgtAFnjxLsppxIRbgJHNuZL7Y+py+7kZjT7xS+wOmCxYTn4O7IqLqkvekg99MDVQ==} '@types/stack-utils@2.0.3': - resolution: - { - integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==, - } + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} '@types/superagent@8.1.9': - resolution: - { - integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==, - } + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} '@types/supertest@7.2.0': - resolution: - { - integrity: sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==, - } + resolution: {integrity: sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==} '@types/testing-library__jest-dom@5.14.9': - resolution: - { - integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==, - } + resolution: {integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==} '@types/tough-cookie@4.0.5': - resolution: - { - integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==, - } + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} '@types/ws@8.18.1': - resolution: - { - integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==, - } + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} '@types/yargs-parser@21.0.3': - resolution: - { - integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==, - } + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} '@types/yargs@15.0.20': - resolution: - { - integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==, - } + resolution: {integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==} '@types/yargs@17.0.35': - resolution: - { - integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==, - } + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} '@typescript-eslint/eslint-plugin@8.59.4': - resolution: - { - integrity: sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.59.4 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/parser@8.59.4': - resolution: - { - integrity: sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/project-service@8.59.4': - resolution: - { - integrity: sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/scope-manager@8.59.4': - resolution: - { - integrity: sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.59.4': - resolution: - { - integrity: sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/type-utils@8.59.4': - resolution: - { - integrity: sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/types@8.59.4': - resolution: - { - integrity: sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.59.4': - resolution: - { - integrity: sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/utils@8.59.4': - resolution: - { - integrity: sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/visitor-keys@8.59.4': - resolution: - { - integrity: sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.1': - resolution: - { - integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==, - } + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} '@unrs/resolver-binding-android-arm-eabi@1.12.2': - resolution: - { - integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==, - } + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} cpu: [arm] os: [android] '@unrs/resolver-binding-android-arm64@1.12.2': - resolution: - { - integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==, - } + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} cpu: [arm64] os: [android] '@unrs/resolver-binding-darwin-arm64@1.12.2': - resolution: - { - integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==, - } + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} cpu: [arm64] os: [darwin] '@unrs/resolver-binding-darwin-x64@1.12.2': - resolution: - { - integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==, - } + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} cpu: [x64] os: [darwin] '@unrs/resolver-binding-freebsd-x64@1.12.2': - resolution: - { - integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==, - } + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} cpu: [x64] os: [freebsd] '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': - resolution: - { - integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==, - } + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': - resolution: - { - integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==, - } + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': - resolution: - { - integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==, - } + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.12.2': - resolution: - { - integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==, - } + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': - resolution: - { - integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==, - } + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} cpu: [loong64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-loong64-musl@1.12.2': - resolution: - { - integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==, - } + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} cpu: [loong64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': - resolution: - { - integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==, - } + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': - resolution: - { - integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==, - } + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': - resolution: - { - integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==, - } + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': - resolution: - { - integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==, - } + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.12.2': - resolution: - { - integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==, - } + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.12.2': - resolution: - { - integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==, - } + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-openharmony-arm64@1.12.2': - resolution: - { - integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==, - } + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} cpu: [arm64] os: [openharmony] '@unrs/resolver-binding-wasm32-wasi@1.12.2': - resolution: - { - integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} cpu: [wasm32] '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': - resolution: - { - integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==, - } + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} cpu: [arm64] os: [win32] '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': - resolution: - { - integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==, - } + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} cpu: [ia32] os: [win32] '@unrs/resolver-binding-win32-x64-msvc@1.12.2': - resolution: - { - integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==, - } + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} cpu: [x64] os: [win32] '@vitejs/plugin-react@4.7.0': - resolution: - { - integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==, - } - engines: { node: ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 '@yarnpkg/lockfile@1.1.0': - resolution: - { - integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==, - } + resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} '@yarnpkg/parsers@3.0.2': - resolution: - { - integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==, - } - engines: { node: '>=18.12.0' } + resolution: {integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==} + engines: {node: '>=18.12.0'} '@zkochan/js-yaml@0.0.7': - resolution: - { - integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==, - } + resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} hasBin: true JSONStream@1.3.5: - resolution: - { - integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==, - } + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true abbrev@2.0.0: - resolution: - { - integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} accept-language-parser@1.5.0: - resolution: - { - integrity: sha512-QhyTbMLYo0BBGg1aWbeMG4ekWtds/31BrEU+DONOg/7ax23vxpL03Pb7/zBmha2v7vdD3AyzZVWBVGEZxKOXWw==, - } + resolution: {integrity: sha512-QhyTbMLYo0BBGg1aWbeMG4ekWtds/31BrEU+DONOg/7ax23vxpL03Pb7/zBmha2v7vdD3AyzZVWBVGEZxKOXWw==} accepts@2.0.0: - resolution: - { - integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} acorn-jsx@5.3.2: - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn-walk@8.3.4: - resolution: - { - integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==, - } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} acorn@8.15.0: - resolution: - { - integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, - } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} hasBin: true add-stream@1.0.0: - resolution: - { - integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==, - } + resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} agent-base@7.1.4: - resolution: - { - integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} aggregate-error@3.1.0: - resolution: - { - integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} ajv@6.12.6: - resolution: - { - integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, - } + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ajv@8.20.0: - resolution: - { - integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==, - } + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} ansi-colors@4.1.3: - resolution: - { - integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} ansi-escapes@4.3.2: - resolution: - { - integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} ansi-regex@6.2.2: - resolution: - { - integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} ansi-styles@5.2.0: - resolution: - { - integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} ansi-styles@6.2.3: - resolution: - { - integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} anymatch@3.1.3: - resolution: - { - integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} appstash@0.7.0: - resolution: - { - integrity: sha512-UExc8kEseReJRbllAkQ/qeW+jHb4iVFR8bLfggSLvSO7LwiVjQWfnQxN+ToLkVBKqMbIENrLUTvynMSEC73xUg==, - } + resolution: {integrity: sha512-UExc8kEseReJRbllAkQ/qeW+jHb4iVFR8bLfggSLvSO7LwiVjQWfnQxN+ToLkVBKqMbIENrLUTvynMSEC73xUg==} aproba@2.0.0: - resolution: - { - integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==, - } + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} arg@4.1.3: - resolution: - { - integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==, - } + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} argparse@1.0.10: - resolution: - { - integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==, - } + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} aria-hidden@1.2.6: - resolution: - { - integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} aria-query@4.2.2: - resolution: - { - integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==, - } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} + engines: {node: '>=6.0'} aria-query@5.3.0: - resolution: - { - integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, - } + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} array-differ@3.0.0: - resolution: - { - integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} + engines: {node: '>=8'} array-ify@1.0.0: - resolution: - { - integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==, - } + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} array-union@2.1.0: - resolution: - { - integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} arrify@1.0.1: - resolution: - { - integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} arrify@2.0.1: - resolution: - { - integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} asap@2.0.6: - resolution: - { - integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==, - } + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} async-retry@1.3.3: - resolution: - { - integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==, - } + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} async@3.2.6: - resolution: - { - integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==, - } + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} asynckit@0.4.0: - resolution: - { - integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, - } + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} atob@2.1.2: - resolution: - { - integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==, - } - engines: { node: '>= 4.5.0' } + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} hasBin: true axios@1.13.2: - resolution: - { - integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==, - } + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} babel-jest@30.4.1: - resolution: - { - integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 babel-plugin-istanbul@7.0.1: - resolution: - { - integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + engines: {node: '>=12'} babel-plugin-jest-hoist@30.4.0: - resolution: - { - integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} babel-plugin-styled-components@2.1.4: - resolution: - { - integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==, - } + resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==} peerDependencies: styled-components: '>= 2' babel-preset-current-node-syntax@1.2.0: - resolution: - { - integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==, - } + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 babel-preset-jest@30.4.0: - resolution: - { - integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-beta.1 babel-runtime@6.25.0: - resolution: - { - integrity: sha512-zeCYxDePWYAT/DfmQWIHsMSFW2vv45UIwIAMjGvQVsTd47RwsiRH0uK1yzyWZ7LDBKdhnGDPM6NYEO5CZyhPrg==, - } + resolution: {integrity: sha512-zeCYxDePWYAT/DfmQWIHsMSFW2vv45UIwIAMjGvQVsTd47RwsiRH0uK1yzyWZ7LDBKdhnGDPM6NYEO5CZyhPrg==} balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} balanced-match@4.0.4: - resolution: - { - integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} base-64@1.0.0: - resolution: - { - integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==, - } + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} base64-js@1.5.1: - resolution: - { - integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, - } + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} baseline-browser-mapping@2.9.15: - resolution: - { - integrity: sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==, - } + resolution: {integrity: sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==} hasBin: true before-after-hook@2.2.3: - resolution: - { - integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==, - } + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} big-integer@1.6.52: - resolution: - { - integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} bin-links@4.0.4: - resolution: - { - integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} binary-extensions@2.3.0: - resolution: - { - integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} bl@4.1.0: - resolution: - { - integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==, - } + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} body-parser@2.2.1: - resolution: - { - integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + engines: {node: '>=18'} boolbase@1.0.0: - resolution: - { - integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, - } + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} bowser@2.14.1: - resolution: - { - integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==, - } + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} brace-expansion@1.1.12: - resolution: - { - integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, - } + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} brace-expansion@2.0.2: - resolution: - { - integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==, - } + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} brace-expansion@2.1.0: - resolution: - { - integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==, - } + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} brace-expansion@5.0.4: - resolution: - { - integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} brace-expansion@5.0.6: - resolution: - { - integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} braces@3.0.3: - resolution: - { - integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} broadcast-channel@3.7.0: - resolution: - { - integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==, - } + resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} browserslist@4.28.1: - resolution: - { - integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true bs-logger@0.2.6: - resolution: - { - integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} bser@2.1.1: - resolution: - { - integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==, - } + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} buffer-equal-constant-time@1.0.1: - resolution: - { - integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==, - } + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} buffer-from@1.1.2: - resolution: - { - integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, - } + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} buffer@5.6.0: - resolution: - { - integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==, - } + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} buffer@5.7.1: - resolution: - { - integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==, - } + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} busboy@0.3.1: - resolution: - { - integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==, - } - engines: { node: '>=4.5.0' } + resolution: {integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==} + engines: {node: '>=4.5.0'} byte-size@8.1.1: - resolution: - { - integrity: sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==, - } - engines: { node: '>=12.17' } + resolution: {integrity: sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==} + engines: {node: '>=12.17'} bytes@3.1.2: - resolution: - { - integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} cacache@18.0.4: - resolution: - { - integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} + engines: {node: ^16.14.0 || >=18.0.0} call-bind-apply-helpers@1.0.2: - resolution: - { - integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} call-bound@1.0.4: - resolution: - { - integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} camel-case@3.0.0: - resolution: - { - integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==, - } + resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} camelcase-keys@6.2.2: - resolution: - { - integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} camelcase@5.3.1: - resolution: - { - integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} camelcase@6.3.0: - resolution: - { - integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} camelize@1.0.1: - resolution: - { - integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==, - } + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} caniuse-lite@1.0.30001765: - resolution: - { - integrity: sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==, - } + resolution: {integrity: sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==} case@1.6.3: - resolution: - { - integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} + engines: {node: '>= 0.8.0'} chalk@3.0.0: - resolution: - { - integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} chalk@4.1.0: - resolution: - { - integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==} + engines: {node: '>=10'} chalk@4.1.2: - resolution: - { - integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} char-regex@1.0.2: - resolution: - { - integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} chardet@2.1.1: - resolution: - { - integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==, - } + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} cheerio-select@2.1.0: - resolution: - { - integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==, - } + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} cheerio@1.0.0-rc.3: - resolution: - { - integrity: sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==} + engines: {node: '>= 0.6'} cheerio@1.1.2: - resolution: - { - integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==, - } - engines: { node: '>=20.18.1' } + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} chokidar@3.6.0: - resolution: - { - integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==, - } - engines: { node: '>= 8.10.0' } + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} chownr@2.0.0: - resolution: - { - integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} ci-info@3.9.0: - resolution: - { - integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} ci-info@4.3.1: - resolution: - { - integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} ci-info@4.4.0: - resolution: - { - integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} cjs-module-lexer@2.2.0: - resolution: - { - integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==, - } + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} clean-ansi@0.2.1: - resolution: - { - integrity: sha512-V9IOKBKHv0Sqay4efImuCVWwO8kVmaKU66cvbDBa99F21j2G3ye3mrKkPSfZ7RTR7yP4CNDjtESkNY5dFq+8Sg==, - } + resolution: {integrity: sha512-V9IOKBKHv0Sqay4efImuCVWwO8kVmaKU66cvbDBa99F21j2G3ye3mrKkPSfZ7RTR7yP4CNDjtESkNY5dFq+8Sg==} clean-css@4.2.4: - resolution: - { - integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==, - } - engines: { node: '>= 4.0' } + resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} + engines: {node: '>= 4.0'} clean-stack@2.2.0: - resolution: - { - integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} cli-cursor@3.1.0: - resolution: - { - integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} cli-spinners@2.6.1: - resolution: - { - integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + engines: {node: '>=6'} cli-spinners@2.9.2: - resolution: - { - integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} cli-width@3.0.0: - resolution: - { - integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} cliui@6.0.0: - resolution: - { - integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==, - } + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} cliui@7.0.4: - resolution: - { - integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==, - } + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} cliui@8.0.1: - resolution: - { - integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} clone-deep@4.0.1: - resolution: - { - integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} clone@1.0.4: - resolution: - { - integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==, - } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} clsx@1.2.1: - resolution: - { - integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} clsx@2.1.1: - resolution: - { - integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} cmd-shim@6.0.3: - resolution: - { - integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} co@4.6.0: - resolution: - { - integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==, - } - engines: { iojs: '>= 1.0.0', node: '>= 0.12.0' } + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} collect-v8-coverage@1.0.3: - resolution: - { - integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==, - } + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: '>=7.0.0' } + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} color-support@1.1.3: - resolution: - { - integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==, - } + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true columnify@1.6.0: - resolution: - { - integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==, - } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} + engines: {node: '>=8.0.0'} combined-stream@1.0.8: - resolution: - { - integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} commander@10.0.1: - resolution: - { - integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} commander@2.17.1: - resolution: - { - integrity: sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==, - } + resolution: {integrity: sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==} commander@2.19.0: - resolution: - { - integrity: sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==, - } + resolution: {integrity: sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==} commander@5.1.0: - resolution: - { - integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} common-ancestor-path@1.0.1: - resolution: - { - integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==, - } + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} compare-func@2.0.0: - resolution: - { - integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==, - } + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} component-emitter@1.3.1: - resolution: - { - integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==, - } + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} concat-stream@2.0.0: - resolution: - { - integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==, - } - engines: { '0': node >= 6.0 } + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} config-chain@1.1.13: - resolution: - { - integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==, - } + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} console-control-strings@1.1.0: - resolution: - { - integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==, - } + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} content-disposition@1.0.1: - resolution: - { - integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} content-type@1.0.5: - resolution: - { - integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} conventional-changelog-angular@7.0.0: - resolution: - { - integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} conventional-changelog-core@5.0.1: - resolution: - { - integrity: sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==} + engines: {node: '>=14'} conventional-changelog-preset-loader@3.0.0: - resolution: - { - integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==} + engines: {node: '>=14'} conventional-changelog-writer@6.0.1: - resolution: - { - integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==} + engines: {node: '>=14'} hasBin: true conventional-commits-filter@3.0.0: - resolution: - { - integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==} + engines: {node: '>=14'} conventional-commits-parser@4.0.0: - resolution: - { - integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==} + engines: {node: '>=14'} hasBin: true conventional-recommended-bump@7.0.1: - resolution: - { - integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==} + engines: {node: '>=14'} hasBin: true convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} cookie-parser@1.4.7: - resolution: - { - integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==} + engines: {node: '>= 0.8.0'} cookie-signature@1.0.6: - resolution: - { - integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==, - } + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} cookie-signature@1.2.2: - resolution: - { - integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==, - } - engines: { node: '>=6.6.0' } + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} cookie@0.7.2: - resolution: - { - integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} cookiejar@2.1.4: - resolution: - { - integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==, - } + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} copyfiles@2.4.1: - resolution: - { - integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==, - } + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} hasBin: true core-js-pure@3.47.0: - resolution: - { - integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==, - } + resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} core-js@2.6.12: - resolution: - { - integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==, - } + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. core-util-is@1.0.3: - resolution: - { - integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, - } + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} cors@2.8.6: - resolution: - { - integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} cosmiconfig@9.0.0: - resolution: - { - integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} peerDependencies: typescript: '>=4.9.5' peerDependenciesMeta: @@ -8854,142 +6822,79 @@ packages: optional: true create-require@1.1.1: - resolution: - { - integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==, - } + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} cron-parser@4.9.0: - resolution: - { - integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + engines: {node: '>=12.0.0'} cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} css-color-keywords@1.0.0: - resolution: - { - integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} css-select@1.2.0: - resolution: - { - integrity: sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==, - } + resolution: {integrity: sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==} css-select@5.2.2: - resolution: - { - integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==, - } + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} css-to-react-native@3.2.0: - resolution: - { - integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==, - } + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} css-what@2.1.3: - resolution: - { - integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==, - } + resolution: {integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==} css-what@6.2.2: - resolution: - { - integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} css.escape@1.5.1: - resolution: - { - integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, - } + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} css@3.0.0: - resolution: - { - integrity: sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==, - } + resolution: {integrity: sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==} cssesc@3.0.0: - resolution: - { - integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} hasBin: true cssstyle@4.6.0: - resolution: - { - integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} csstype@3.2.3: - resolution: - { - integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, - } + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} csv-parse@6.2.1: - resolution: - { - integrity: sha512-LRLMV+UCyfMokp8Wb411duBf1gaBKJfOfBWU9eHMJ+b+cJYZsNu3AFmjJf3+yPGd59Exz1TsMjaSFyxnYB9+IQ==, - } + resolution: {integrity: sha512-LRLMV+UCyfMokp8Wb411duBf1gaBKJfOfBWU9eHMJ+b+cJYZsNu3AFmjJf3+yPGd59Exz1TsMjaSFyxnYB9+IQ==} csv-parser@3.2.1: - resolution: - { - integrity: sha512-v8RPMSglouR9od735SnwSxLBbCJqEPSbgm1R5qfr8yIiMUCEFjox56kRZid0SvgHJEkxeIEu3+a9QS3YRh7CuA==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-v8RPMSglouR9od735SnwSxLBbCJqEPSbgm1R5qfr8yIiMUCEFjox56kRZid0SvgHJEkxeIEu3+a9QS3YRh7CuA==} + engines: {node: '>= 10'} hasBin: true dargs@7.0.0: - resolution: - { - integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} data-urls@5.0.0: - resolution: - { - integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} dateformat@3.0.3: - resolution: - { - integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==, - } + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} debounce-promise@3.1.2: - resolution: - { - integrity: sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==, - } + resolution: {integrity: sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==} debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -8997,37 +6902,22 @@ packages: optional: true decamelize-keys@1.1.1: - resolution: - { - integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} decamelize@1.2.0: - resolution: - { - integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} decimal.js@10.6.0: - resolution: - { - integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, - } + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} decode-uri-component@0.2.2: - resolution: - { - integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} dedent@1.5.3: - resolution: - { - integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==, - } + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -9035,10 +6925,7 @@ packages: optional: true dedent@1.7.2: - resolution: - { - integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==, - } + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -9046,237 +6933,129 @@ packages: optional: true deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} deepmerge@4.3.1: - resolution: - { - integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} defaults@1.0.4: - resolution: - { - integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==, - } + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} define-lazy-prop@2.0.0: - resolution: - { - integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} delayed-stream@1.0.0: - resolution: - { - integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, - } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} depd@1.1.2: - resolution: - { - integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} depd@2.0.0: - resolution: - { - integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} deprecation@2.3.1: - resolution: - { - integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==, - } + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dequal@2.0.3: - resolution: - { - integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} detect-indent@5.0.0: - resolution: - { - integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==} + engines: {node: '>=4'} detect-newline@3.1.0: - resolution: - { - integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} detect-node-es@1.1.0: - resolution: - { - integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==, - } + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} detect-node@2.1.0: - resolution: - { - integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==, - } + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} dezalgo@1.0.4: - resolution: - { - integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==, - } + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} dicer@0.3.0: - resolution: - { - integrity: sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==, - } - engines: { node: '>=4.5.0' } + resolution: {integrity: sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==} + engines: {node: '>=4.5.0'} diff-sequences@29.6.3: - resolution: - { - integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} diff@4.0.2: - resolution: - { - integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==, - } - engines: { node: '>=0.3.1' } + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} dom-accessibility-api@0.5.16: - resolution: - { - integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, - } + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dom-serializer@0.1.1: - resolution: - { - integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==, - } + resolution: {integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==} dom-serializer@0.2.2: - resolution: - { - integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==, - } + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} dom-serializer@1.4.1: - resolution: - { - integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==, - } + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} dom-serializer@2.0.0: - resolution: - { - integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, - } + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} domelementtype@1.3.1: - resolution: - { - integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==, - } + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} domelementtype@2.3.0: - resolution: - { - integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, - } + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} domhandler@2.4.2: - resolution: - { - integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==, - } + resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} domhandler@3.3.0: - resolution: - { - integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} + engines: {node: '>= 4'} domhandler@4.3.1: - resolution: - { - integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} domhandler@5.0.3: - resolution: - { - integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} domutils@1.5.1: - resolution: - { - integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==, - } + resolution: {integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==} domutils@1.7.0: - resolution: - { - integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==, - } + resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} domutils@2.8.0: - resolution: - { - integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==, - } + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} domutils@3.2.2: - resolution: - { - integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==, - } + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} dot-prop@5.3.0: - resolution: - { - integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} dotenv-expand@11.0.7: - resolution: - { - integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} + engines: {node: '>=12'} dotenv@16.4.7: - resolution: - { - integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} drizzle-orm@0.45.2: - resolution: - { - integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==, - } + resolution: {integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=4' @@ -9368,270 +7147,153 @@ packages: optional: true dunder-proto@1.0.1: - resolution: - { - integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} eastasianwidth@0.2.0: - resolution: - { - integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, - } + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} ecdsa-sig-formatter@1.0.11: - resolution: - { - integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==, - } + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} editorconfig@1.0.4: - resolution: - { - integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} hasBin: true ee-first@1.1.1: - resolution: - { - integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==, - } + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} ejs@3.1.10: - resolution: - { - integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} hasBin: true electron-to-chromium@1.5.267: - resolution: - { - integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==, - } + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emittery@0.13.1: - resolution: - { - integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} emoji-regex@8.0.0: - resolution: - { - integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, - } + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} emoji-regex@9.2.2: - resolution: - { - integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, - } + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} encodeurl@2.0.0: - resolution: - { - integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} encoding-sniffer@0.2.1: - resolution: - { - integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==, - } + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} encoding@0.1.13: - resolution: - { - integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==, - } + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} end-of-stream@1.4.5: - resolution: - { - integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==, - } + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} enquirer@2.3.6: - resolution: - { - integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} entities@1.1.2: - resolution: - { - integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==, - } + resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} entities@2.2.0: - resolution: - { - integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, - } + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} entities@4.5.0: - resolution: - { - integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, - } - engines: { node: '>=0.12' } + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} entities@6.0.1: - resolution: - { - integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, - } - engines: { node: '>=0.12' } + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} env-paths@2.2.1: - resolution: - { - integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} envalid@8.1.1: - resolution: - { - integrity: sha512-vOUfHxAFFvkBjbVQbBfgnCO9d3GcNfMMTtVfgqSU2rQGMFEVqWy9GBuoSfHnwGu7EqR0/GeukQcL3KjFBaga9w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-vOUfHxAFFvkBjbVQbBfgnCO9d3GcNfMMTtVfgqSU2rQGMFEVqWy9GBuoSfHnwGu7EqR0/GeukQcL3KjFBaga9w==} + engines: {node: '>=18'} envinfo@7.13.0: - resolution: - { - integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} + engines: {node: '>=4'} hasBin: true err-code@2.0.3: - resolution: - { - integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==, - } + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} error-ex@1.3.4: - resolution: - { - integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==, - } + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} es-define-property@1.0.1: - resolution: - { - integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} es-errors@1.3.0: - resolution: - { - integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} es-object-atoms@1.1.1: - resolution: - { - integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: - resolution: - { - integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} esbuild@0.25.12: - resolution: - { - integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} hasBin: true esbuild@0.27.2: - resolution: - { - integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} escape-goat@3.0.0: - resolution: - { - integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} + engines: {node: '>=10'} escape-html@1.0.3: - resolution: - { - integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==, - } + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} escape-string-regexp@1.0.5: - resolution: - { - integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} escape-string-regexp@2.0.0: - resolution: - { - integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} escape-string-regexp@4.0.0: - resolution: - { - integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} eslint-config-prettier@10.1.8: - resolution: - { - integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==, - } + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' eslint-plugin-simple-import-sort@12.1.1: - resolution: - { - integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==, - } + resolution: {integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==} peerDependencies: eslint: '>=5.0.0' eslint-plugin-unused-imports@4.4.1: - resolution: - { - integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==, - } + resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 eslint: ^10.0.0 || ^9.0.0 || ^8.0.0 @@ -9640,39 +7302,24 @@ packages: optional: true eslint-scope@8.4.0: - resolution: - { - integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-visitor-keys@4.2.1: - resolution: - { - integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@5.0.1: - resolution: - { - integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint@9.39.2: - resolution: - { - integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: jiti: '*' @@ -9681,190 +7328,106 @@ packages: optional: true espree@10.4.0: - resolution: - { - integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: - resolution: - { - integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} hasBin: true esquery@1.6.0: - resolution: - { - integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} etag@1.8.1: - resolution: - { - integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} eventemitter3@4.0.7: - resolution: - { - integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==, - } + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} eventemitter3@5.0.4: - resolution: - { - integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==, - } + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} events@3.3.0: - resolution: - { - integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==, - } - engines: { node: '>=0.8.x' } + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} execa@5.0.0: - resolution: - { - integrity: sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==} + engines: {node: '>=10'} execa@5.1.1: - resolution: - { - integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} exit-x@0.2.2: - resolution: - { - integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + engines: {node: '>= 0.8.0'} expect@30.2.0: - resolution: - { - integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} expect@30.4.1: - resolution: - { - integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} exponential-backoff@3.1.3: - resolution: - { - integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==, - } + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} express@5.2.1: - resolution: - { - integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-glob@3.3.3: - resolution: - { - integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, - } - engines: { node: '>=8.6.0' } + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} fast-safe-stringify@2.1.1: - resolution: - { - integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==, - } + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} fast-uri@3.1.2: - resolution: - { - integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==, - } + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} fast-xml-builder@1.2.0: - resolution: - { - integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==, - } + resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} fast-xml-parser@5.7.3: - resolution: - { - integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==, - } + resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} hasBin: true fastq@1.20.1: - resolution: - { - integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, - } + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fb-watchman@2.0.2: - resolution: - { - integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==, - } + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -9872,92 +7435,53 @@ packages: optional: true figures@3.2.0: - resolution: - { - integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} file-entry-cache@8.0.0: - resolution: - { - integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} filelist@1.0.4: - resolution: - { - integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==, - } + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} fill-range@7.1.1: - resolution: - { - integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} finalhandler@2.1.1: - resolution: - { - integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==, - } - engines: { node: '>= 18.0.0' } + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} find-and-require-package-json@0.9.1: - resolution: - { - integrity: sha512-jFpCL0XgjipSk109viUtfp+NyR/oW6a4Xus4tV3UYkmCbsjisEeZD1x5QnD1NDDK/hXas1WFs4yO13L4TPXWlQ==, - } + resolution: {integrity: sha512-jFpCL0XgjipSk109viUtfp+NyR/oW6a4Xus4tV3UYkmCbsjisEeZD1x5QnD1NDDK/hXas1WFs4yO13L4TPXWlQ==} find-up@2.1.0: - resolution: - { - integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} find-up@4.1.0: - resolution: - { - integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} flat-cache@4.0.1: - resolution: - { - integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flat@5.0.2: - resolution: - { - integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==, - } + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true flatted@3.3.3: - resolution: - { - integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==, - } + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} follow-redirects@1.15.11: - resolution: - { - integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} peerDependencies: debug: '*' peerDependenciesMeta: @@ -9965,38 +7489,23 @@ packages: optional: true foreground-child@3.3.1: - resolution: - { - integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} form-data@4.0.5: - resolution: - { - integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} formidable@3.5.4: - resolution: - { - integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} forwarded@0.2.0: - resolution: - { - integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} framer-motion@12.36.0: - resolution: - { - integrity: sha512-4PqYHAT7gev0ke0wos+PyrcFxI0HScjm3asgU8nSYa8YzJFuwgIvdj3/s3ZaxLq0bUSboIn19A2WS/MHwLCvfw==, - } + resolution: {integrity: sha512-4PqYHAT7gev0ke0wos+PyrcFxI0HScjm3asgU8nSYa8YzJFuwgIvdj3/s3ZaxLq0bUSboIn19A2WS/MHwLCvfw==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -10010,299 +7519,173 @@ packages: optional: true fresh@2.0.0: - resolution: - { - integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} front-matter@4.0.2: - resolution: - { - integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==, - } + resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} fs-capacitor@6.2.0: - resolution: - { - integrity: sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==} + engines: {node: '>=10'} fs-capacitor@8.0.0: - resolution: - { - integrity: sha512-+Lk6iSKajdGw+7XYxUkwIzreJ2G1JFlYOdnKJv5PzwFLVsoJYBpCuS7WPIUSNT1IbQaEWT1nhYU63Ud03DyzLA==, - } - engines: { node: ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-+Lk6iSKajdGw+7XYxUkwIzreJ2G1JFlYOdnKJv5PzwFLVsoJYBpCuS7WPIUSNT1IbQaEWT1nhYU63Ud03DyzLA==} + engines: {node: ^14.17.0 || >=16.0.0} fs-constants@1.0.0: - resolution: - { - integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==, - } + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} fs-extra@11.3.3: - resolution: - { - integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==, - } - engines: { node: '>=14.14' } + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + engines: {node: '>=14.14'} fs-minipass@2.1.0: - resolution: - { - integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} fs-minipass@3.0.3: - resolution: - { - integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} fs.realpath@1.0.0: - resolution: - { - integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, - } + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} fsevents@2.3.2: - resolution: - { - integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: - { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, - } + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} genomic@5.6.0: - resolution: - { - integrity: sha512-5VimMClR8yfML+kxmaFAYgPbGqIJ9fhQduNv2V0vnCj+8BqjOyfOnX21jvp0GIkKQSyUZh0YUO5ewMKQqbUEfg==, - } + resolution: {integrity: sha512-5VimMClR8yfML+kxmaFAYgPbGqIJ9fhQduNv2V0vnCj+8BqjOyfOnX21jvp0GIkKQSyUZh0YUO5ewMKQqbUEfg==} gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} get-caller-file@2.0.5: - resolution: - { - integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, - } - engines: { node: 6.* || 8.* || >= 10.* } + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} get-intrinsic@1.3.0: - resolution: - { - integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} get-nonce@1.0.1: - resolution: - { - integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} get-package-type@0.1.0: - resolution: - { - integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==, - } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} get-pkg-repo@4.2.1: - resolution: - { - integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} + engines: {node: '>=6.9.0'} hasBin: true get-port@5.1.1: - resolution: - { - integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} get-proto@1.0.1: - resolution: - { - integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} get-stream@6.0.0: - resolution: - { - integrity: sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==} + engines: {node: '>=10'} get-stream@6.0.1: - resolution: - { - integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} get-tsconfig@4.13.0: - resolution: - { - integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==, - } + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} get-value@3.0.1: - resolution: - { - integrity: sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==, - } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==} + engines: {node: '>=6.0'} git-raw-commits@3.0.0: - resolution: - { - integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==} + engines: {node: '>=14'} deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. hasBin: true git-remote-origin-url@2.0.0: - resolution: - { - integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} + engines: {node: '>=4'} git-semver-tags@5.0.1: - resolution: - { - integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==} + engines: {node: '>=14'} deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. hasBin: true git-up@7.0.0: - resolution: - { - integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==, - } + resolution: {integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==} git-url-parse@14.0.0: - resolution: - { - integrity: sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==, - } + resolution: {integrity: sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==} gitconfiglocal@1.0.0: - resolution: - { - integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==, - } + resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} glob@10.5.0: - resolution: - { - integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==, - } + resolution: {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 hasBin: true glob@11.1.0: - resolution: - { - integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} 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 hasBin: true glob@13.0.6: - resolution: - { - integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} glob@7.2.3: - resolution: - { - integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==, - } + resolution: {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 glob@9.3.5: - resolution: - { - integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} 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 globals@14.0.0: - resolution: - { - integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} gopd@1.2.0: - resolution: - { - integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} grafast@1.0.2: - resolution: - { - integrity: sha512-E3PH6hfOfhlJxMmiBplWs3KDmJTIQr/iZ9hs4c73k3pLk9Tx2nSdVNBVIe7D1stejgPbyC6wRxLfXtJmDx2P7A==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-E3PH6hfOfhlJxMmiBplWs3KDmJTIQr/iZ9hs4c73k3pLk9Tx2nSdVNBVIe7D1stejgPbyC6wRxLfXtJmDx2P7A==} + engines: {node: '>=22'} peerDependencies: '@envelop/core': ^5.0.0 graphql: 16.13.0 @@ -10311,11 +7694,8 @@ packages: optional: true grafserv@1.0.0: - resolution: - { - integrity: sha512-9w0zwYSHS10DfHOAQhaCVvJnOFuk+YY+nZZqG0ZOqFbner3Zf4GvqfWlNETdmUQdB6dnISfGZCkIaSZt5R7wCQ==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-9w0zwYSHS10DfHOAQhaCVvJnOFuk+YY+nZZqG0ZOqFbner3Zf4GvqfWlNETdmUQdB6dnISfGZCkIaSZt5R7wCQ==} + engines: {node: '>=22'} peerDependencies: '@envelop/core': ^5.0.0 '@whatwg-node/server': ^0.9.64 @@ -10338,11 +7718,8 @@ packages: optional: true graphile-build-pg@5.0.2: - resolution: - { - integrity: sha512-8vkg1x5UcooFogPCp+3EUt6qqWX8NG01gANoVFUk9yK2c8DW+aQaF37LAaa96r9JR4FE0yeO2zk6WvOOemk2dg==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-8vkg1x5UcooFogPCp+3EUt6qqWX8NG01gANoVFUk9yK2c8DW+aQaF37LAaa96r9JR4FE0yeO2zk6WvOOemk2dg==} + engines: {node: '>=22'} peerDependencies: '@dataplan/pg': ^1.0.0-rc.7 grafast: ^1.0.0-rc.8 @@ -10357,29 +7734,20 @@ packages: optional: true graphile-build@5.0.2: - resolution: - { - integrity: sha512-ELhDDZ2Y3ZWmF+ZziEd9ytMxFnmBoCX4/1My2Jhifr1FUD9IzWFhzH2qAWKmTvDrIy7b6jc4SV23lWmTbiD3ZA==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-ELhDDZ2Y3ZWmF+ZziEd9ytMxFnmBoCX4/1My2Jhifr1FUD9IzWFhzH2qAWKmTvDrIy7b6jc4SV23lWmTbiD3ZA==} + engines: {node: '>=22'} peerDependencies: grafast: ^1.0.0-rc.8 graphile-config: ^1.0.0-rc.5 graphql: 16.13.0 graphile-config@1.0.1: - resolution: - { - integrity: sha512-sVdSWNmetW/WZKVQ0Dii2kCu0Le6X6qwuBRecWg575iOMjbZgxo+b4oVeSOTtR6NTKuLsMYQBkzSaeoAKOPB+A==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-sVdSWNmetW/WZKVQ0Dii2kCu0Le6X6qwuBRecWg575iOMjbZgxo+b4oVeSOTtR6NTKuLsMYQBkzSaeoAKOPB+A==} + engines: {node: '>=22'} graphile-utils@5.0.0: - resolution: - { - integrity: sha512-W/qzi7o6w4cakNqapeGNSGmYq0QowsiT0okPdmdUmZe117XiGPACzL+H0vqG0ZqkLUjZ5vNTIOGYQVqJ4Tg6KA==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-W/qzi7o6w4cakNqapeGNSGmYq0QowsiT0okPdmdUmZe117XiGPACzL+H0vqG0ZqkLUjZ5vNTIOGYQVqJ4Tg6KA==} + engines: {node: '>=22'} peerDependencies: '@dataplan/pg': ^1.0.0-rc.7 grafast: ^1.0.0-rc.8 @@ -10393,11 +7761,8 @@ packages: optional: true graphile-utils@5.0.1: - resolution: - { - integrity: sha512-FtJgxL2BDv1B417sOCsNdu1e3yZkZY7jPMlMHTvzcJLc/7o9rDh+ucJGDmLiKe5Z4lS8KXxVRLZWbxC56/RHcw==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-FtJgxL2BDv1B417sOCsNdu1e3yZkZY7jPMlMHTvzcJLc/7o9rDh+ucJGDmLiKe5Z4lS8KXxVRLZWbxC56/RHcw==} + engines: {node: '>=22'} peerDependencies: '@dataplan/pg': ^1.0.0-rc.7 grafast: ^1.0.0-rc.8 @@ -10411,56 +7776,38 @@ packages: optional: true graphiql@5.2.2: - resolution: - { - integrity: sha512-qYhw7e2QPLPEIdJXqlLa/XkZtEu2SVYyD71abOpPnrzmJzTdB+QsEswFIMg9u1WGkEtp/wi8epCsuKeA/chRcg==, - } + resolution: {integrity: sha512-qYhw7e2QPLPEIdJXqlLa/XkZtEu2SVYyD71abOpPnrzmJzTdB+QsEswFIMg9u1WGkEtp/wi8epCsuKeA/chRcg==} peerDependencies: graphql: 16.13.0 react: ^18 || ^19 react-dom: ^18 || ^19 graphql-language-service@5.5.0: - resolution: - { - integrity: sha512-9EvWrLLkF6Y5e29/2cmFoAO6hBPPAZlCyjznmpR11iFtRydfkss+9m6x+htA8h7YznGam+TtJwS6JuwoWWgb2Q==, - } + resolution: {integrity: sha512-9EvWrLLkF6Y5e29/2cmFoAO6hBPPAZlCyjznmpR11iFtRydfkss+9m6x+htA8h7YznGam+TtJwS6JuwoWWgb2Q==} hasBin: true peerDependencies: graphql: 16.13.0 graphql-request@7.4.0: - resolution: - { - integrity: sha512-xfr+zFb/QYbs4l4ty0dltqiXIp07U6sl+tOKAb0t50/EnQek6CVVBLjETXi+FghElytvgaAWtIOt3EV7zLzIAQ==, - } + resolution: {integrity: sha512-xfr+zFb/QYbs4l4ty0dltqiXIp07U6sl+tOKAb0t50/EnQek6CVVBLjETXi+FghElytvgaAWtIOt3EV7zLzIAQ==} peerDependencies: graphql: 16.13.0 graphql-tag@2.12.6: - resolution: - { - integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} peerDependencies: graphql: 16.13.0 graphql-upload@13.0.0: - resolution: - { - integrity: sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >= 16.0.0 } + resolution: {integrity: sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA==} + engines: {node: ^12.22.0 || ^14.17.0 || >= 16.0.0} peerDependencies: graphql: 16.13.0 graphql-ws@6.0.8: - resolution: - { - integrity: sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw==, - } - engines: { node: '>=20' } + resolution: {integrity: sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw==} + engines: {node: '>=20'} peerDependencies: '@fastify/websocket': ^10 || ^11 crossws: ~0.3 @@ -10475,636 +7822,357 @@ packages: optional: true graphql@16.13.0: - resolution: - { - integrity: sha512-uSisMYERbaB9bkA9M4/4dnqyktaEkf1kMHNKq/7DHyxVeWqHQ2mBmVqm5u6/FVHwF3iCNalKcg82Zfl+tffWoA==, - } - engines: { node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0 } + resolution: {integrity: sha512-uSisMYERbaB9bkA9M4/4dnqyktaEkf1kMHNKq/7DHyxVeWqHQ2mBmVqm5u6/FVHwF3iCNalKcg82Zfl+tffWoA==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} handlebars@4.7.9: - resolution: - { - integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==, - } - engines: { node: '>=0.4.7' } + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + engines: {node: '>=0.4.7'} hasBin: true hard-rejection@2.1.0: - resolution: - { - integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} has-flag@3.0.0: - resolution: - { - integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} has-symbols@1.1.0: - resolution: - { - integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} has-tostringtag@1.0.2: - resolution: - { - integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} has-unicode@2.0.1: - resolution: - { - integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==, - } + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} hasown@2.0.2: - resolution: - { - integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} he@1.2.0: - resolution: - { - integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, - } + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true hoist-non-react-statics@3.3.2: - resolution: - { - integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==, - } + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} hosted-git-info@2.8.9: - resolution: - { - integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==, - } + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} hosted-git-info@4.1.0: - resolution: - { - integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} hosted-git-info@7.0.2: - resolution: - { - integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} html-encoding-sniffer@4.0.0: - resolution: - { - integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} html-escaper@2.0.2: - resolution: - { - integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==, - } + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} html-minifier@3.5.21: - resolution: - { - integrity: sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==} + engines: {node: '>=4'} hasBin: true htmlparser2@10.0.0: - resolution: - { - integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==, - } + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} htmlparser2@3.10.1: - resolution: - { - integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==, - } + resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} htmlparser2@4.1.0: - resolution: - { - integrity: sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==, - } + resolution: {integrity: sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==} http-cache-semantics@4.2.0: - resolution: - { - integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==, - } + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} http-errors@1.8.1: - resolution: - { - integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} http-errors@2.0.1: - resolution: - { - integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} http-proxy-agent@7.0.2: - resolution: - { - integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} http-proxy@1.18.1: - resolution: - { - integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==, - } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} https-proxy-agent@7.0.6: - resolution: - { - integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} human-signals@2.1.0: - resolution: - { - integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==, - } - engines: { node: '>=10.17.0' } + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} iconv-lite@0.6.3: - resolution: - { - integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} iconv-lite@0.7.1: - resolution: - { - integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + engines: {node: '>=0.10.0'} ieee754@1.2.1: - resolution: - { - integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, - } + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} ignore-by-default@1.0.1: - resolution: - { - integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==, - } + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} ignore-walk@6.0.5: - resolution: - { - integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} ignore@5.3.2: - resolution: - { - integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} ignore@7.0.5: - resolution: - { - integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} import-fresh@3.3.1: - resolution: - { - integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} import-local@3.1.0: - resolution: - { - integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} hasBin: true import-local@3.2.0: - resolution: - { - integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} hasBin: true imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: '>=0.8.19' } + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} indent-string@4.0.0: - resolution: - { - integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} inflection@3.0.2: - resolution: - { - integrity: sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==} + engines: {node: '>=18.0.0'} inflekt@0.7.1: - resolution: - { - integrity: sha512-iNsb7kpQeo7HUHayGI8Wbe9PC1TIJu15VfJU/Q6MADuhZh6skVifGrDsJp8t45xXg84ywvqnZwh4B6lN34nVTw==, - } + resolution: {integrity: sha512-iNsb7kpQeo7HUHayGI8Wbe9PC1TIJu15VfJU/Q6MADuhZh6skVifGrDsJp8t45xXg84ywvqnZwh4B6lN34nVTw==} inflight@1.0.6: - resolution: - { - integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, - } + resolution: {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. inherits@2.0.4: - resolution: - { - integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, - } + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: - resolution: - { - integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==, - } + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} ini@4.1.3: - resolution: - { - integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} init-package-json@6.0.3: - resolution: - { - integrity: sha512-Zfeb5ol+H+eqJWHTaGca9BovufyGeIfr4zaaBorPmJBMrJ+KBnN+kQx2ZtXdsotUTgldHmHQV44xvUWOUA7E2w==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-Zfeb5ol+H+eqJWHTaGca9BovufyGeIfr4zaaBorPmJBMrJ+KBnN+kQx2ZtXdsotUTgldHmHQV44xvUWOUA7E2w==} + engines: {node: ^16.14.0 || >=18.0.0} inquirer@8.2.7: - resolution: - { - integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} + engines: {node: '>=12.0.0'} inquirerer@4.8.1: - resolution: - { - integrity: sha512-X8cPy91JMH6EmUPUqgnxc+oYssHdQlitWR23youH2208F2enxElCKc6Mt/5H8KAupYDgOuRuyBO+SRaRXStj8A==, - } + resolution: {integrity: sha512-X8cPy91JMH6EmUPUqgnxc+oYssHdQlitWR23youH2208F2enxElCKc6Mt/5H8KAupYDgOuRuyBO+SRaRXStj8A==} interpret@3.1.1: - resolution: - { - integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==, - } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} ip-address@10.1.0: - resolution: - { - integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==, - } - engines: { node: '>= 12' } + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} ipaddr.js@1.9.1: - resolution: - { - integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} ipv6-normalize@1.0.1: - resolution: - { - integrity: sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA==, - } + resolution: {integrity: sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA==} is-arrayish@0.2.1: - resolution: - { - integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, - } + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} is-binary-path@2.1.0: - resolution: - { - integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} is-ci@3.0.1: - resolution: - { - integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==, - } + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true is-core-module@2.16.1: - resolution: - { - integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} is-docker@2.2.1: - resolution: - { - integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} hasBin: true is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-fullwidth-code-point@3.0.0: - resolution: - { - integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} is-generator-fn@2.1.0: - resolution: - { - integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-interactive@1.0.0: - resolution: - { - integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} is-lambda@1.0.1: - resolution: - { - integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==, - } + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} is-obj@2.0.0: - resolution: - { - integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} is-plain-obj@1.1.0: - resolution: - { - integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} is-plain-object@2.0.4: - resolution: - { - integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} is-potential-custom-element-name@1.0.1: - resolution: - { - integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, - } + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} is-primitive@3.0.1: - resolution: - { - integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} + engines: {node: '>=0.10.0'} is-promise@4.0.0: - resolution: - { - integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==, - } + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} is-ssh@1.4.1: - resolution: - { - integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==, - } + resolution: {integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==} is-stream@2.0.0: - resolution: - { - integrity: sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==} + engines: {node: '>=8'} is-stream@2.0.1: - resolution: - { - integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} is-text-path@1.0.1: - resolution: - { - integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} is-unicode-supported@0.1.0: - resolution: - { - integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} is-wsl@2.2.0: - resolution: - { - integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} isarray@0.0.1: - resolution: - { - integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==, - } + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} isarray@1.0.0: - resolution: - { - integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, - } + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} isexe@3.1.1: - resolution: - { - integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} isobject@3.0.1: - resolution: - { - integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} istanbul-lib-coverage@3.2.2: - resolution: - { - integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} istanbul-lib-instrument@6.0.3: - resolution: - { - integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} istanbul-lib-report@3.0.1: - resolution: - { - integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} istanbul-lib-source-maps@5.0.6: - resolution: - { - integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} istanbul-reports@3.2.0: - resolution: - { - integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} iterall@1.3.0: - resolution: - { - integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==, - } + resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==} jackspeak@3.4.3: - resolution: - { - integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==, - } + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} jackspeak@4.2.3: - resolution: - { - integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} jake@10.9.4: - resolution: - { - integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} hasBin: true jest-changed-files@30.4.1: - resolution: - { - integrity: sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-circus@30.4.2: - resolution: - { - integrity: sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-cli@30.4.2: - resolution: - { - integrity: sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -11113,11 +8181,8 @@ packages: optional: true jest-config@30.4.2: - resolution: - { - integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@types/node': '*' esbuild-register: '>=3.4.0' @@ -11131,46 +8196,28 @@ packages: optional: true jest-diff@29.7.0: - resolution: - { - integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-diff@30.2.0: - resolution: - { - integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-diff@30.4.1: - resolution: - { - integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-docblock@30.4.0: - resolution: - { - integrity: sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-each@30.4.1: - resolution: - { - integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-environment-jsdom@30.4.1: - resolution: - { - integrity: sha512-o3nfaN4zej7qgk2X0j8Jhq/S9nAVKs2xK3QeQxeHVvpkEPxaA1yxDGydR+iVI7zPy7Cp62Aq2h3Ja46QvfWHGA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-o3nfaN4zej7qgk2X0j8Jhq/S9nAVKs2xK3QeQxeHVvpkEPxaA1yxDGydR+iVI7zPy7Cp62Aq2h3Ja46QvfWHGA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -11178,88 +8225,52 @@ packages: optional: true jest-environment-node@30.4.1: - resolution: - { - integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-get-type@29.6.3: - resolution: - { - integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-haste-map@30.4.1: - resolution: - { - integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-in-case@1.0.2: - resolution: - { - integrity: sha512-2DE6Gdwnh5jkCYTePWoQinF+zne3lCADibXoYJEt8PS84JaRug0CyAOrEgzMxbzln3YcSY2PBeru7ct4tbflYA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-2DE6Gdwnh5jkCYTePWoQinF+zne3lCADibXoYJEt8PS84JaRug0CyAOrEgzMxbzln3YcSY2PBeru7ct4tbflYA==} + engines: {node: '>=4'} jest-leak-detector@30.4.1: - resolution: - { - integrity: sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-matcher-utils@30.2.0: - resolution: - { - integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-matcher-utils@30.4.1: - resolution: - { - integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-message-util@30.2.0: - resolution: - { - integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-message-util@30.4.1: - resolution: - { - integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-mock@30.2.0: - resolution: - { - integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-mock@30.4.1: - resolution: - { - integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-pnp-resolver@1.2.3: - resolution: - { - integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} peerDependencies: jest-resolve: '*' peerDependenciesMeta: @@ -11267,95 +8278,56 @@ packages: optional: true jest-regex-util@30.0.1: - resolution: - { - integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-regex-util@30.4.0: - resolution: - { - integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-resolve-dependencies@30.4.2: - resolution: - { - integrity: sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-resolve@30.4.1: - resolution: - { - integrity: sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runner@30.4.2: - resolution: - { - integrity: sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runtime@30.4.2: - resolution: - { - integrity: sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-snapshot@30.4.1: - resolution: - { - integrity: sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-util@30.2.0: - resolution: - { - integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-util@30.4.1: - resolution: - { - integrity: sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-validate@30.4.1: - resolution: - { - integrity: sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-watcher@30.4.1: - resolution: - { - integrity: sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-worker@30.4.1: - resolution: - { - integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest@30.4.2: - resolution: - { - integrity: sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -11364,66 +8336,39 @@ packages: optional: true jiti@2.7.0: - resolution: - { - integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==, - } + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true js-beautify@1.15.4: - resolution: - { - integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} hasBin: true js-cookie@3.0.5: - resolution: - { - integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} js-sha3@0.8.0: - resolution: - { - integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==, - } + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-yaml@3.14.2: - resolution: - { - integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==, - } + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} hasBin: true js-yaml@4.1.0: - resolution: - { - integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, - } + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true js-yaml@4.1.1: - resolution: - { - integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==, - } + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true jsdom@26.1.0: - resolution: - { - integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -11431,532 +8376,289 @@ packages: optional: true jsesc@3.1.0: - resolution: - { - integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: - resolution: - { - integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, - } + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} json-parse-better-errors@1.0.2: - resolution: - { - integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==, - } + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: - resolution: - { - integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, - } + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} json-parse-even-better-errors@3.0.2: - resolution: - { - integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} json-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-schema-traverse@1.0.0: - resolution: - { - integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, - } + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} json-stable-stringify-without-jsonify@1.0.1: - resolution: - { - integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, - } + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json-stringify-nice@1.1.4: - resolution: - { - integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==, - } + resolution: {integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==} json-stringify-safe@5.0.1: - resolution: - { - integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==, - } + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} hasBin: true jsonc-parser@3.2.0: - resolution: - { - integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==, - } + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} jsonc-parser@3.3.1: - resolution: - { - integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, - } + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} jsonfile@6.2.0: - resolution: - { - integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==, - } + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} jsonparse@1.3.1: - resolution: - { - integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==, - } - engines: { '0': node >= 0.2.0 } + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} jsonwebtoken@9.0.3: - resolution: - { - integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==, - } - engines: { node: '>=12', npm: '>=6' } + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} juice@7.0.0: - resolution: - { - integrity: sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q==, - } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q==} + engines: {node: '>=10.0.0'} hasBin: true just-diff-apply@5.5.0: - resolution: - { - integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==, - } + resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} just-diff@6.0.2: - resolution: - { - integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==, - } + resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==} jwa@2.0.1: - resolution: - { - integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==, - } + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} jws@4.0.1: - resolution: - { - integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==, - } + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} keyv@4.5.4: - resolution: - { - integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, - } + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} kind-of@6.0.3: - resolution: - { - integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} komoji@0.9.0: - resolution: - { - integrity: sha512-mbAwXYrQgSE9r618CzW7BHvQfKmDyvPoJFPzaWimEVfcaTyE9aqCvf5RbOwzP16ranN/4rmAuAme1GMrWRZ/sQ==, - } + resolution: {integrity: sha512-mbAwXYrQgSE9r618CzW7BHvQfKmDyvPoJFPzaWimEVfcaTyE9aqCvf5RbOwzP16ranN/4rmAuAme1GMrWRZ/sQ==} lerna@8.2.4: - resolution: - { - integrity: sha512-0gaVWDIVT7fLfprfwpYcQajb7dBJv3EGavjG7zvJ+TmGx3/wovl5GklnSwM2/WeE0Z2wrIz7ndWhBcDUHVjOcQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-0gaVWDIVT7fLfprfwpYcQajb7dBJv3EGavjG7zvJ+TmGx3/wovl5GklnSwM2/WeE0Z2wrIz7ndWhBcDUHVjOcQ==} + engines: {node: '>=18.0.0'} hasBin: true leven@3.1.0: - resolution: - { - integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} libnpmaccess@8.0.6: - resolution: - { - integrity: sha512-uM8DHDEfYG6G5gVivVl+yQd4pH3uRclHC59lzIbSvy7b5FEwR+mU49Zq1jEyRtRFv7+M99mUW9S0wL/4laT4lw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-uM8DHDEfYG6G5gVivVl+yQd4pH3uRclHC59lzIbSvy7b5FEwR+mU49Zq1jEyRtRFv7+M99mUW9S0wL/4laT4lw==} + engines: {node: ^16.14.0 || >=18.0.0} libnpmpublish@9.0.9: - resolution: - { - integrity: sha512-26zzwoBNAvX9AWOPiqqF6FG4HrSCPsHFkQm7nT+xU1ggAujL/eae81RnCv4CJ2In9q9fh10B88sYSzKCUh/Ghg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-26zzwoBNAvX9AWOPiqqF6FG4HrSCPsHFkQm7nT+xU1ggAujL/eae81RnCv4CJ2In9q9fh10B88sYSzKCUh/Ghg==} + engines: {node: ^16.14.0 || >=18.0.0} libpg-query@17.7.3: - resolution: - { - integrity: sha512-lHKBvoWRsXt/9bJxpAeFxkLu0CA6tELusqy3o1z6/DwGXSETxhKJDaNlNdrNV8msvXDLBhpg/4RE/fKKs5rYFA==, - } + resolution: {integrity: sha512-lHKBvoWRsXt/9bJxpAeFxkLu0CA6tELusqy3o1z6/DwGXSETxhKJDaNlNdrNV8msvXDLBhpg/4RE/fKKs5rYFA==} lines-and-columns@1.2.4: - resolution: - { - integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, - } + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} lines-and-columns@2.0.3: - resolution: - { - integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} linkify-it@5.0.0: - resolution: - { - integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==, - } + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} load-json-file@4.0.0: - resolution: - { - integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} load-json-file@6.2.0: - resolution: - { - integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} + engines: {node: '>=8'} locate-path@2.0.0: - resolution: - { - integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} locate-path@5.0.0: - resolution: - { - integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} lodash.includes@4.3.0: - resolution: - { - integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==, - } + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} lodash.isboolean@3.0.3: - resolution: - { - integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==, - } + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} lodash.isinteger@4.0.4: - resolution: - { - integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==, - } + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} lodash.ismatch@4.4.0: - resolution: - { - integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==, - } + resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} lodash.isnumber@3.0.3: - resolution: - { - integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==, - } + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} lodash.isplainobject@4.0.6: - resolution: - { - integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==, - } + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} lodash.isstring@4.0.1: - resolution: - { - integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==, - } + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} lodash.memoize@4.1.2: - resolution: - { - integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==, - } + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} lodash.merge@4.6.2: - resolution: - { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, - } + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} lodash.once@4.1.1: - resolution: - { - integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==, - } + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} lodash@4.17.21: - resolution: - { - integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, - } + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} lodash@4.18.1: - resolution: - { - integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==, - } + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} log-symbols@4.1.0: - resolution: - { - integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} long-timeout@0.1.1: - resolution: - { - integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==, - } + resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} long@5.3.2: - resolution: - { - integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==, - } + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} loose-envify@1.4.0: - resolution: - { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, - } + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true lower-case@1.1.4: - resolution: - { - integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==, - } + resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} lru-cache@10.4.3: - resolution: - { - integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, - } + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@11.2.7: - resolution: - { - integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lru-cache@6.0.0: - resolution: - { - integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} luxon@3.7.2: - resolution: - { - integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + engines: {node: '>=12'} lz-string@1.5.0: - resolution: - { - integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, - } + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true mailgun.js@10.4.0: - resolution: - { - integrity: sha512-YrdaZEAJwwjXGBTfZTNQ1LM7tmkdUaz2NpZEu7+zULcG4Wrlhd7cWSNZW0bxT3bP48k5N0mZWz8C2f9gc2+Geg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-YrdaZEAJwwjXGBTfZTNQ1LM7tmkdUaz2NpZEu7+zULcG4Wrlhd7cWSNZW0bxT3bP48k5N0mZWz8C2f9gc2+Geg==} + engines: {node: '>=18.0.0'} makage@0.3.0: - resolution: - { - integrity: sha512-HiBTpSy6iLnI+apv91QTWgBlFiePOCAUXmdYwlVzqkfwtUc0aM1FNu70CLs8xvc2u/R43m3J1SuWprMcKpV+vw==, - } + resolution: {integrity: sha512-HiBTpSy6iLnI+apv91QTWgBlFiePOCAUXmdYwlVzqkfwtUc0aM1FNu70CLs8xvc2u/R43m3J1SuWprMcKpV+vw==} hasBin: true make-dir@2.1.0: - resolution: - { - integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} make-dir@4.0.0: - resolution: - { - integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} make-error@1.3.6: - resolution: - { - integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==, - } + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} make-fetch-happen@13.0.1: - resolution: - { - integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==} + engines: {node: ^16.14.0 || >=18.0.0} makeerror@1.0.12: - resolution: - { - integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==, - } + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} map-obj@1.0.1: - resolution: - { - integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} map-obj@4.3.0: - resolution: - { - integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} markdown-it@14.1.1: - resolution: - { - integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==, - } + resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} hasBin: true match-sorter@6.3.4: - resolution: - { - integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==, - } + resolution: {integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==} math-intrinsics@1.1.0: - resolution: - { - integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} mdurl@2.0.0: - resolution: - { - integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==, - } + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} media-typer@1.1.0: - resolution: - { - integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} mensch@0.3.4: - resolution: - { - integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==, - } + resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} meow@8.1.2: - resolution: - { - integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} merge-descriptors@2.0.0: - resolution: - { - integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} merge-stream@2.0.0: - resolution: - { - integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==, - } + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} meros@1.3.2: - resolution: - { - integrity: sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A==, - } - engines: { node: '>=13' } + resolution: {integrity: sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A==} + engines: {node: '>=13'} peerDependencies: '@types/node': '>=13' peerDependenciesMeta: @@ -11964,572 +8666,314 @@ packages: optional: true methods@1.1.2: - resolution: - { - integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} micromatch@4.0.8: - resolution: - { - integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} microseconds@0.2.0: - resolution: - { - integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==, - } + resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} mime-db@1.52.0: - resolution: - { - integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} mime-db@1.54.0: - resolution: - { - integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} mime-types@2.1.35: - resolution: - { - integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} mime-types@3.0.2: - resolution: - { - integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} mime@2.6.0: - resolution: - { - integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==, - } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} hasBin: true mimic-fn@2.1.0: - resolution: - { - integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} min-indent@1.0.1: - resolution: - { - integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} minimatch@10.2.4: - resolution: - { - integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} minimatch@10.2.5: - resolution: - { - integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} minimatch@3.0.5: - resolution: - { - integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==, - } + resolution: {integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==} minimatch@3.1.2: - resolution: - { - integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, - } + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} minimatch@3.1.5: - resolution: - { - integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, - } + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} minimatch@5.1.9: - resolution: - { - integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} minimatch@8.0.7: - resolution: - { - integrity: sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==} + engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.1: - resolution: - { - integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.3: - resolution: - { - integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.9: - resolution: - { - integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} minimist-options@4.1.0: - resolution: - { - integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} minimist@1.2.8: - resolution: - { - integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, - } + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} minipass-collect@2.0.1: - resolution: - { - integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} minipass-fetch@3.0.5: - resolution: - { - integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} minipass-flush@1.0.5: - resolution: - { - integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} minipass-pipeline@1.2.4: - resolution: - { - integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} minipass-sized@1.0.3: - resolution: - { - integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} minipass@3.3.6: - resolution: - { - integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} minipass@4.2.8: - resolution: - { - integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} minipass@5.0.0: - resolution: - { - integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} minipass@7.1.2: - resolution: - { - integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} minipass@7.1.3: - resolution: - { - integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} minizlib@2.1.2: - resolution: - { - integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} mjml-accordion@4.7.1: - resolution: - { - integrity: sha512-oYwC/CLOUWJ6pRt2saDHj/HytGOHO5B5lKNqUAhKPye5HFNZykKEV5ChmZ2NfGsGU+9BhQ7H5DaCafp4fDmPAg==, - } + resolution: {integrity: sha512-oYwC/CLOUWJ6pRt2saDHj/HytGOHO5B5lKNqUAhKPye5HFNZykKEV5ChmZ2NfGsGU+9BhQ7H5DaCafp4fDmPAg==} mjml-body@4.7.1: - resolution: - { - integrity: sha512-JCrkit+kjCfQyKuVyWSOonM2LGs/o3+63R9l2SleFeXf3+0CaKWaZr/Exzvaeo28c+1o3yRqXbJIpD22SEtJfQ==, - } + resolution: {integrity: sha512-JCrkit+kjCfQyKuVyWSOonM2LGs/o3+63R9l2SleFeXf3+0CaKWaZr/Exzvaeo28c+1o3yRqXbJIpD22SEtJfQ==} mjml-button@4.7.1: - resolution: - { - integrity: sha512-N3WkTMPOvKw2y6sakt1YfYDbOB8apumm1OApPG6J18CHcrX03BwhHPrdfu1JwlRNGwx4kCDdb6zNCGPwuZxkCg==, - } + resolution: {integrity: sha512-N3WkTMPOvKw2y6sakt1YfYDbOB8apumm1OApPG6J18CHcrX03BwhHPrdfu1JwlRNGwx4kCDdb6zNCGPwuZxkCg==} mjml-carousel@4.7.1: - resolution: - { - integrity: sha512-eH3rRyX23ES0BKOn+UUV39+yGNmZVApBVVV0A5znDaNWskCg6/g6ZhEHi4nkWpj+aP2lJKI0HX1nrMfJg0Mxhg==, - } + resolution: {integrity: sha512-eH3rRyX23ES0BKOn+UUV39+yGNmZVApBVVV0A5znDaNWskCg6/g6ZhEHi4nkWpj+aP2lJKI0HX1nrMfJg0Mxhg==} mjml-cli@4.7.1: - resolution: - { - integrity: sha512-xzCtJVKYVhGorvTmnbcMUfZlmJdBnu1UBD9A1H8UUBGMNE/Hs9QpHs9PLCMp8JR/uhSu15IgVjhFN0oSVndMRQ==, - } + resolution: {integrity: sha512-xzCtJVKYVhGorvTmnbcMUfZlmJdBnu1UBD9A1H8UUBGMNE/Hs9QpHs9PLCMp8JR/uhSu15IgVjhFN0oSVndMRQ==} hasBin: true mjml-column@4.7.1: - resolution: - { - integrity: sha512-CGw81TnGiuPR1GblLOez8xeoeAz1SEFjMpqapazjgXUuF5xUxg3qH55Wt4frpXe3VypeZWVYeumr6CwoNaPbKg==, - } + resolution: {integrity: sha512-CGw81TnGiuPR1GblLOez8xeoeAz1SEFjMpqapazjgXUuF5xUxg3qH55Wt4frpXe3VypeZWVYeumr6CwoNaPbKg==} mjml-core@4.7.1: - resolution: - { - integrity: sha512-AMACoq/h440m7SM86As8knW0bNQgjNIzsP/cMF6X9RO07GfszgbaWUq/XCaRNi+q8bWvBJSCXbngDJySVc5ALw==, - } + resolution: {integrity: sha512-AMACoq/h440m7SM86As8knW0bNQgjNIzsP/cMF6X9RO07GfszgbaWUq/XCaRNi+q8bWvBJSCXbngDJySVc5ALw==} mjml-divider@4.7.1: - resolution: - { - integrity: sha512-7+uCUJdqEr6w8AzpF8lhRheelYEgOwiK0KJGlAQN3LF+h2S1rTPEzEB67qL2x5cU+80kPlxtxoQWImDBy0vXqg==, - } + resolution: {integrity: sha512-7+uCUJdqEr6w8AzpF8lhRheelYEgOwiK0KJGlAQN3LF+h2S1rTPEzEB67qL2x5cU+80kPlxtxoQWImDBy0vXqg==} mjml-group@4.7.1: - resolution: - { - integrity: sha512-mAYdhocCzetdhPSws/9/sQ4hcz4kQPX2dNitQmbxNVwoMFYXjp/WcLEfGc5u13Ue7dPfcV6c9lB/Uu5o3NmRvw==, - } + resolution: {integrity: sha512-mAYdhocCzetdhPSws/9/sQ4hcz4kQPX2dNitQmbxNVwoMFYXjp/WcLEfGc5u13Ue7dPfcV6c9lB/Uu5o3NmRvw==} mjml-head-attributes@4.7.1: - resolution: - { - integrity: sha512-nB/bQ3I98Dvy/IkI4nqxTCnLonULkIKc8KrieRTrtPkUV3wskBzngpCgnjKvFPbHWiGlwjHDzcFJc7G0uWeqog==, - } + resolution: {integrity: sha512-nB/bQ3I98Dvy/IkI4nqxTCnLonULkIKc8KrieRTrtPkUV3wskBzngpCgnjKvFPbHWiGlwjHDzcFJc7G0uWeqog==} mjml-head-breakpoint@4.7.1: - resolution: - { - integrity: sha512-0KB5SweIWDvwHkn4VCUsEhCQgfY/0wkNUnSXNoftaRujv0NQFQfOOH4eINy0NZYfDfrE4WYe08z+olHprp+T2A==, - } + resolution: {integrity: sha512-0KB5SweIWDvwHkn4VCUsEhCQgfY/0wkNUnSXNoftaRujv0NQFQfOOH4eINy0NZYfDfrE4WYe08z+olHprp+T2A==} mjml-head-font@4.7.1: - resolution: - { - integrity: sha512-9YGzBcQ2htZ6j266fiLLfzcxqDEDLTvfKtypTjaeRb1w3N8S5wL+/zJA5ZjRL6r39Ij5ZPQSlSDC32KPiwhGkA==, - } + resolution: {integrity: sha512-9YGzBcQ2htZ6j266fiLLfzcxqDEDLTvfKtypTjaeRb1w3N8S5wL+/zJA5ZjRL6r39Ij5ZPQSlSDC32KPiwhGkA==} mjml-head-html-attributes@4.7.1: - resolution: - { - integrity: sha512-2TK2nGpq4rGaghbVx2UNm5TXeZ5BTGYEvtSPoYPNu02KRCj6tb+uedAgFXwJpX+ogRfIfPK50ih+9ZMoHwf2IQ==, - } + resolution: {integrity: sha512-2TK2nGpq4rGaghbVx2UNm5TXeZ5BTGYEvtSPoYPNu02KRCj6tb+uedAgFXwJpX+ogRfIfPK50ih+9ZMoHwf2IQ==} mjml-head-preview@4.7.1: - resolution: - { - integrity: sha512-UHlvvgldiPDODq/5zKMsmXgRb/ZyKygKDUVQSM5bm3HvpKXeyYxJZazcIGmlGICEqv1ced1WGINhCg72dSfN+Q==, - } + resolution: {integrity: sha512-UHlvvgldiPDODq/5zKMsmXgRb/ZyKygKDUVQSM5bm3HvpKXeyYxJZazcIGmlGICEqv1ced1WGINhCg72dSfN+Q==} mjml-head-style@4.7.1: - resolution: - { - integrity: sha512-8Gij99puN1SoOx5tGBjgkh4iCpI+zbwGBiB2Y8VwJrwXQxdJ1Qa902dQP5djoFFG39Bthii/48cS/d1bHigGPQ==, - } + resolution: {integrity: sha512-8Gij99puN1SoOx5tGBjgkh4iCpI+zbwGBiB2Y8VwJrwXQxdJ1Qa902dQP5djoFFG39Bthii/48cS/d1bHigGPQ==} mjml-head-title@4.7.1: - resolution: - { - integrity: sha512-vK3r+DApTXw2EoK/fh8dQOsO438Z7Ksy6iBIb7h04x33d4Z41r6+jtgxGXoKFXnjgr8MyLX5HZyyie5obW+hZg==, - } + resolution: {integrity: sha512-vK3r+DApTXw2EoK/fh8dQOsO438Z7Ksy6iBIb7h04x33d4Z41r6+jtgxGXoKFXnjgr8MyLX5HZyyie5obW+hZg==} mjml-head@4.7.1: - resolution: - { - integrity: sha512-jUcJ674CT1oT8NTQWTjQQBFZu4yklK0oppfGFJ1cq76ze3isMiyhSnGnOHw6FkjLnZtb3gXXaGKX7UZM+UMk/w==, - } + resolution: {integrity: sha512-jUcJ674CT1oT8NTQWTjQQBFZu4yklK0oppfGFJ1cq76ze3isMiyhSnGnOHw6FkjLnZtb3gXXaGKX7UZM+UMk/w==} mjml-hero@4.7.1: - resolution: - { - integrity: sha512-x+29V8zJAs8EV/eTtGbR921pCpitMQOAkyvNANW/3JLDTL2Oio1OYvGPVC3z1wOT9LKuRTxVzNHVt/bBw02CSQ==, - } + resolution: {integrity: sha512-x+29V8zJAs8EV/eTtGbR921pCpitMQOAkyvNANW/3JLDTL2Oio1OYvGPVC3z1wOT9LKuRTxVzNHVt/bBw02CSQ==} mjml-image@4.7.1: - resolution: - { - integrity: sha512-l3uRR2jaM0Bpz4ctdWuxQUFgg+ol6Nt+ODOrnHsGMwpmFOh4hTPTky6KaF0LCXxYmGbI0FoGBna+hVNnkBsQCA==, - } + resolution: {integrity: sha512-l3uRR2jaM0Bpz4ctdWuxQUFgg+ol6Nt+ODOrnHsGMwpmFOh4hTPTky6KaF0LCXxYmGbI0FoGBna+hVNnkBsQCA==} mjml-migrate@4.7.1: - resolution: - { - integrity: sha512-RgrJ9fHg6iRHC2H4pjRDWilBQ1eTH2jRu1ayDplbnepGoql83vLZaYaWc5Q+J+NsaNI16x+bgNB3fQdBiK+mng==, - } + resolution: {integrity: sha512-RgrJ9fHg6iRHC2H4pjRDWilBQ1eTH2jRu1ayDplbnepGoql83vLZaYaWc5Q+J+NsaNI16x+bgNB3fQdBiK+mng==} hasBin: true mjml-navbar@4.7.1: - resolution: - { - integrity: sha512-awdu8zT7xhS+9aCVunqtocUs8KA2xb+UhJ8UGbxVBpYbTNj3rCL9aWUXqWVwMk1la+3ypCkFuDuTl6dIoWPWlA==, - } + resolution: {integrity: sha512-awdu8zT7xhS+9aCVunqtocUs8KA2xb+UhJ8UGbxVBpYbTNj3rCL9aWUXqWVwMk1la+3ypCkFuDuTl6dIoWPWlA==} mjml-parser-xml@4.7.1: - resolution: - { - integrity: sha512-UWfuRpN45k3GUEv2yl8n5Uf98Tg6FyCsyRnqZGo83mgZzlJRDYTdKII9RjZM646/S8+Q8e9qxi3AsL00j6sZsQ==, - } + resolution: {integrity: sha512-UWfuRpN45k3GUEv2yl8n5Uf98Tg6FyCsyRnqZGo83mgZzlJRDYTdKII9RjZM646/S8+Q8e9qxi3AsL00j6sZsQ==} mjml-raw@4.7.1: - resolution: - { - integrity: sha512-mCQFEXINTkC8i7ydP1Km99e0FaZTeu79AoYnTBAILd4QO+RuD3n/PimBGrcGrOUex0JIKa2jyVQOcSCBuG4WpA==, - } + resolution: {integrity: sha512-mCQFEXINTkC8i7ydP1Km99e0FaZTeu79AoYnTBAILd4QO+RuD3n/PimBGrcGrOUex0JIKa2jyVQOcSCBuG4WpA==} mjml-react@1.0.59: - resolution: - { - integrity: sha512-W1ULnMlxJHE0kNpInu+u3CHr6+QcvhoLJ2ov93Pzt2A1wXAv4CJ9T/P5h/BhZn8vvCXgGizcwHv8sfANfQONVw==, - } + resolution: {integrity: sha512-W1ULnMlxJHE0kNpInu+u3CHr6+QcvhoLJ2ov93Pzt2A1wXAv4CJ9T/P5h/BhZn8vvCXgGizcwHv8sfANfQONVw==} peerDependencies: mjml: ^4.1.2 react: ^16.4.0 react-dom: ^16.4.0 mjml-section@4.7.1: - resolution: - { - integrity: sha512-PlhCMsl/bpFwwgQGUopi9OgOGWgRPpEJVKE8hk4He8GXzbfIuDj4DZ9QJSkwIoZ0fZtcgz11Wwb19i9BZcozVw==, - } + resolution: {integrity: sha512-PlhCMsl/bpFwwgQGUopi9OgOGWgRPpEJVKE8hk4He8GXzbfIuDj4DZ9QJSkwIoZ0fZtcgz11Wwb19i9BZcozVw==} mjml-social@4.7.1: - resolution: - { - integrity: sha512-tN/6V3m59izO9rqWpUokHxhwkk2GHkltzIlhI936hAJHh8hFyEO6+ZwQBZm738G00qgfICmQvX5FNq4upkCYjw==, - } + resolution: {integrity: sha512-tN/6V3m59izO9rqWpUokHxhwkk2GHkltzIlhI936hAJHh8hFyEO6+ZwQBZm738G00qgfICmQvX5FNq4upkCYjw==} mjml-spacer@4.7.1: - resolution: - { - integrity: sha512-gQu1+nA9YGnoolfNPvzfVe/RJ8WqS8ho0hthlhiLOC2RnEnmqH7HHSzCFXm4OeN0VgvDQsM7mfYQGl82O58Y+g==, - } + resolution: {integrity: sha512-gQu1+nA9YGnoolfNPvzfVe/RJ8WqS8ho0hthlhiLOC2RnEnmqH7HHSzCFXm4OeN0VgvDQsM7mfYQGl82O58Y+g==} mjml-table@4.7.1: - resolution: - { - integrity: sha512-rPkOtufMiVreb7I7vXk6rDm9i1DXncODnM5JJNhA9Z1dAQwXiz6V5904gAi2cEYfe0M2m0XQ8P5ZCtvqxGkfGA==, - } + resolution: {integrity: sha512-rPkOtufMiVreb7I7vXk6rDm9i1DXncODnM5JJNhA9Z1dAQwXiz6V5904gAi2cEYfe0M2m0XQ8P5ZCtvqxGkfGA==} mjml-text@4.7.1: - resolution: - { - integrity: sha512-hrjxbY59v6hu/Pn0NO+6TMlrdAlRa3M7GVALx/YWYV3hi59zjYfot8Au7Xq64XdcbcI4eiBVbP/AVr8w03HsOw==, - } + resolution: {integrity: sha512-hrjxbY59v6hu/Pn0NO+6TMlrdAlRa3M7GVALx/YWYV3hi59zjYfot8Au7Xq64XdcbcI4eiBVbP/AVr8w03HsOw==} mjml-validator@4.7.1: - resolution: - { - integrity: sha512-Qxubbz5WE182iLSTd/XRuezMr6UE7/u73grDCw0bTIcQsaTAIkWQn2tBI3jj0chWOw+sxwK2C6zPm9B0Cv7BGA==, - } + resolution: {integrity: sha512-Qxubbz5WE182iLSTd/XRuezMr6UE7/u73grDCw0bTIcQsaTAIkWQn2tBI3jj0chWOw+sxwK2C6zPm9B0Cv7BGA==} mjml-wrapper@4.7.1: - resolution: - { - integrity: sha512-6i+ZATUyqIO5YBnx+RFKZ3+6mg3iOCS/EdXGYZSonZ/EHqlt+RJa3fG2BB4dacXqAjghfl6Lk+bLoR47P3xYIQ==, - } + resolution: {integrity: sha512-6i+ZATUyqIO5YBnx+RFKZ3+6mg3iOCS/EdXGYZSonZ/EHqlt+RJa3fG2BB4dacXqAjghfl6Lk+bLoR47P3xYIQ==} mjml@4.7.1: - resolution: - { - integrity: sha512-nwMrmhTI+Aeh9Gav9LHX/i8k8yDi/QpX5h535BlT5oP4NaAUmyxP/UeYUn9yxtPcIzDlM5ullFnRv/71jyHpkQ==, - } + resolution: {integrity: sha512-nwMrmhTI+Aeh9Gav9LHX/i8k8yDi/QpX5h535BlT5oP4NaAUmyxP/UeYUn9yxtPcIzDlM5ullFnRv/71jyHpkQ==} hasBin: true mkdirp@1.0.4: - resolution: - { - integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} hasBin: true mock-req@0.2.0: - resolution: - { - integrity: sha512-IUuwS0W5GjoPyjhuXPQJXpaHfHW7UYFRia8Cchm/xRuyDDclpSQdEoakt3krOpSYvgVlQsbnf0ePDsTRDfp7Dg==, - } + resolution: {integrity: sha512-IUuwS0W5GjoPyjhuXPQJXpaHfHW7UYFRia8Cchm/xRuyDDclpSQdEoakt3krOpSYvgVlQsbnf0ePDsTRDfp7Dg==} modify-values@1.0.1: - resolution: - { - integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} + engines: {node: '>=0.10.0'} monaco-editor@0.52.2: - resolution: - { - integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==, - } + resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} monaco-graphql@1.7.3: - resolution: - { - integrity: sha512-6LAIcg/vT2NGLjHnT+5iIZONsZCaCuz2orbg7qD/u4Ry9R7rDotLh0HAzIF/yKdzEA5fTZC+TofSx2O+Zi+0ow==, - } + resolution: {integrity: sha512-6LAIcg/vT2NGLjHnT+5iIZONsZCaCuz2orbg7qD/u4Ry9R7rDotLh0HAzIF/yKdzEA5fTZC+TofSx2O+Zi+0ow==} peerDependencies: graphql: 16.13.0 monaco-editor: '>= 0.20.0 < 0.53' prettier: ^2.8.0 || ^3.0.0 motion-dom@12.36.0: - resolution: - { - integrity: sha512-Ep1pq8P88rGJ75om8lTCA13zqd7ywPGwCqwuWwin6BKc0hMLkVfcS6qKlRqEo2+t0DwoUcgGJfXwaiFn4AOcQA==, - } + resolution: {integrity: sha512-Ep1pq8P88rGJ75om8lTCA13zqd7ywPGwCqwuWwin6BKc0hMLkVfcS6qKlRqEo2+t0DwoUcgGJfXwaiFn4AOcQA==} motion-utils@12.36.0: - resolution: - { - integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==, - } + resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} multimatch@5.0.0: - resolution: - { - integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} + engines: {node: '>=10'} mute-stream@0.0.8: - resolution: - { - integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==, - } + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} mute-stream@1.0.0: - resolution: - { - integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} nano-time@1.0.0: - resolution: - { - integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==, - } + resolution: {integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==} nanoid@3.3.11: - resolution: - { - integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true napi-postinstall@0.3.4: - resolution: - { - integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==, - } - engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true natural-compare@1.4.0: - resolution: - { - integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, - } + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} negotiator@0.6.4: - resolution: - { - integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} negotiator@1.0.0: - resolution: - { - integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} neo-async@2.6.2: - resolution: - { - integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, - } + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} nested-obj@0.2.2: - resolution: - { - integrity: sha512-M1etu+T6Ai9Bo06L3K3nWD0ytZWltggBGsrxJlOGvMNGlCA4fokUVlbPKoWzsiiRX+PXq6Cb1xFEn4chiyC7MQ==, - } + resolution: {integrity: sha512-M1etu+T6Ai9Bo06L3K3nWD0ytZWltggBGsrxJlOGvMNGlCA4fokUVlbPKoWzsiiRX+PXq6Cb1xFEn4chiyC7MQ==} no-case@2.3.2: - resolution: - { - integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==, - } + resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} node-fetch@2.6.7: - resolution: - { - integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==, - } - engines: { node: 4.x || >=6.0.0 } + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 peerDependenciesMeta: @@ -12537,11 +8981,8 @@ packages: optional: true node-fetch@2.7.0: - resolution: - { - integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==, - } - engines: { node: 4.x || >=6.0.0 } + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 peerDependenciesMeta: @@ -12549,186 +8990,105 @@ packages: optional: true node-gyp@10.3.1: - resolution: - { - integrity: sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==} + engines: {node: ^16.14.0 || >=18.0.0} hasBin: true node-int64@0.4.0: - resolution: - { - integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==, - } + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} node-machine-id@1.1.12: - resolution: - { - integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==, - } + resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} node-releases@2.0.27: - resolution: - { - integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==, - } + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} node-schedule@2.1.1: - resolution: - { - integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==} + engines: {node: '>=6'} nodemailer@6.10.1: - resolution: - { - integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==} + engines: {node: '>=6.0.0'} nodemailer@8.0.5: - resolution: - { - integrity: sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==} + engines: {node: '>=6.0.0'} nodemon@3.1.14: - resolution: - { - integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==} + engines: {node: '>=10'} hasBin: true noms@0.0.0: - resolution: - { - integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==, - } + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} nopt@7.2.1: - resolution: - { - integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true normalize-package-data@2.5.0: - resolution: - { - integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==, - } + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} normalize-package-data@3.0.3: - resolution: - { - integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} normalize-package-data@6.0.2: - resolution: - { - integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} normalize-path@3.0.0: - resolution: - { - integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} npm-bundled@3.0.1: - resolution: - { - integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-install-checks@6.3.0: - resolution: - { - integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-normalize-package-bin@3.0.1: - resolution: - { - integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-package-arg@11.0.2: - resolution: - { - integrity: sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==} + engines: {node: ^16.14.0 || >=18.0.0} npm-packlist@8.0.2: - resolution: - { - integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-pick-manifest@9.1.0: - resolution: - { - integrity: sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==} + engines: {node: ^16.14.0 || >=18.0.0} npm-registry-fetch@17.1.0: - resolution: - { - integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==} + engines: {node: ^16.14.0 || >=18.0.0} npm-run-path@4.0.1: - resolution: - { - integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} nth-check@1.0.2: - resolution: - { - integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==, - } + resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} nth-check@2.1.1: - resolution: - { - integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, - } + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} nullthrows@1.1.1: - resolution: - { - integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==, - } + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} nwsapi@2.2.24: - resolution: - { - integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==, - } + resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} nx@20.8.3: - resolution: - { - integrity: sha512-8w815WSMWar3A/LFzwtmEY+E8cVW62lMiFuPDXje+C8O8hFndfvscP56QHNMn2Zdhz3q0+BZUe+se4Em1BKYdA==, - } + resolution: {integrity: sha512-8w815WSMWar3A/LFzwtmEY+E8cVW62lMiFuPDXje+C8O8hFndfvscP56QHNMn2Zdhz3q0+BZUe+se4Em1BKYdA==} hasBin: true peerDependencies: '@swc-node/register': ^1.8.0 @@ -12740,86 +9100,50 @@ packages: optional: true object-assign@4.1.1: - resolution: - { - integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} object-inspect@1.13.4: - resolution: - { - integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} object-path@0.11.8: - resolution: - { - integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==, - } - engines: { node: '>= 10.12.0' } + resolution: {integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==} + engines: {node: '>= 10.12.0'} oblivious-set@1.0.0: - resolution: - { - integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==, - } + resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} on-finished@2.4.1: - resolution: - { - integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} once@1.4.0: - resolution: - { - integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, - } + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} onetime@5.1.2: - resolution: - { - integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} open@8.4.2: - resolution: - { - integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} optionator@0.9.4: - resolution: - { - integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} ora@5.3.0: - resolution: - { - integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} + engines: {node: '>=10'} ora@5.4.1: - resolution: - { - integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} oxfmt@0.51.0: - resolution: - { - integrity: sha512-l/AoAnaEOV7Q5/Z9kHOMDehVJnCgYN7wRoooWCTUMBMi16BJhLZqd9cmCnwcVFfVlzkt53zK2KLPFNp8vSsoDg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-l/AoAnaEOV7Q5/Z9kHOMDehVJnCgYN7wRoooWCTUMBMi16BJhLZqd9cmCnwcVFfVlzkt53zK2KLPFNp8vSsoDg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: svelte: ^5.0.0 @@ -12828,380 +9152,212 @@ packages: optional: true p-finally@1.0.0: - resolution: - { - integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} p-limit@1.3.0: - resolution: - { - integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} p-limit@2.3.0: - resolution: - { - integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} p-locate@2.0.0: - resolution: - { - integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} p-locate@4.1.0: - resolution: - { - integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} p-map-series@2.1.0: - resolution: - { - integrity: sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==} + engines: {node: '>=8'} p-map@4.0.0: - resolution: - { - integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} p-pipe@3.1.0: - resolution: - { - integrity: sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==} + engines: {node: '>=8'} p-queue@6.6.2: - resolution: - { - integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} p-reduce@2.1.0: - resolution: - { - integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==} + engines: {node: '>=8'} p-timeout@3.2.0: - resolution: - { - integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} p-try@1.0.0: - resolution: - { - integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} p-try@2.2.0: - resolution: - { - integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} p-waterfall@2.1.1: - resolution: - { - integrity: sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw==} + engines: {node: '>=8'} package-json-from-dist@1.0.1: - resolution: - { - integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==, - } + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} pacote@18.0.6: - resolution: - { - integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==} + engines: {node: ^16.14.0 || >=18.0.0} hasBin: true param-case@2.1.1: - resolution: - { - integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==, - } + resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} parse-conflict-json@3.0.1: - resolution: - { - integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} parse-json@4.0.0: - resolution: - { - integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} parse-json@5.2.0: - resolution: - { - integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} parse-package-name@1.0.0: - resolution: - { - integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==, - } + resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==} parse-path@7.1.0: - resolution: - { - integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==, - } + resolution: {integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==} parse-url@8.1.0: - resolution: - { - integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==, - } + resolution: {integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==} parse5-htmlparser2-tree-adapter@7.1.0: - resolution: - { - integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==, - } + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} parse5-parser-stream@7.1.2: - resolution: - { - integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==, - } + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} parse5@3.0.3: - resolution: - { - integrity: sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==, - } + resolution: {integrity: sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==} parse5@7.3.0: - resolution: - { - integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, - } + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} parseurl@1.3.3: - resolution: - { - integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} path-exists@3.0.0: - resolution: - { - integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} path-expression-matcher@1.5.0: - resolution: - { - integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} + engines: {node: '>=14.0.0'} path-is-absolute@1.0.1: - resolution: - { - integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} path-parse@1.0.7: - resolution: - { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, - } + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} path-scurry@1.11.1: - resolution: - { - integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==, - } - engines: { node: '>=16 || 14 >=14.18' } + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-scurry@2.0.2: - resolution: - { - integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} path-to-regexp@8.3.0: - resolution: - { - integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==, - } + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} path-type@3.0.0: - resolution: - { - integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} pg-cloudflare@1.3.0: - resolution: - { - integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==, - } + resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} pg-cloudflare@1.4.0: - resolution: - { - integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==, - } + resolution: {integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==} pg-connection-string@2.12.0: - resolution: - { - integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==, - } + resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==} pg-connection-string@2.13.0: - resolution: - { - integrity: sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==, - } + resolution: {integrity: sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==} pg-copy-streams@7.0.0: - resolution: - { - integrity: sha512-zBvnY6wtaBRE2ae2xXWOOGMaNVPkXh1vhypAkNSKgMdciJeTyIQAHZaEeRAxUjs/p1El5jgzYmwG5u871Zj3dQ==, - } + resolution: {integrity: sha512-zBvnY6wtaBRE2ae2xXWOOGMaNVPkXh1vhypAkNSKgMdciJeTyIQAHZaEeRAxUjs/p1El5jgzYmwG5u871Zj3dQ==} pg-int8@1.0.1: - resolution: - { - integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==, - } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} pg-introspection@1.0.1: - resolution: - { - integrity: sha512-HwxpCEWygpRPfvFf7IVtEPchtjl1Fw0TxzCYXJIQdTEFio/AcGnp2XI5x+LpowbyEa3XgB9L5gvw2D0Jqji4eA==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-HwxpCEWygpRPfvFf7IVtEPchtjl1Fw0TxzCYXJIQdTEFio/AcGnp2XI5x+LpowbyEa3XgB9L5gvw2D0Jqji4eA==} + engines: {node: '>=22'} pg-pool@3.13.0: - resolution: - { - integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==, - } + resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==} peerDependencies: pg: '>=8.0' pg-pool@3.14.0: - resolution: - { - integrity: sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==, - } + resolution: {integrity: sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==} peerDependencies: pg: '>=8.0' pg-proto-parser@1.30.6: - resolution: - { - integrity: sha512-2XwPyl9oz5Pest4ebaovRTTJN8MXaa/XvqMQzKq127fFcl4I1POUgV/FtzHzg/p8FjtO5yHsipeW/kAumzNxxw==, - } + resolution: {integrity: sha512-2XwPyl9oz5Pest4ebaovRTTJN8MXaa/XvqMQzKq127fFcl4I1POUgV/FtzHzg/p8FjtO5yHsipeW/kAumzNxxw==} pg-protocol@1.13.0: - resolution: - { - integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==, - } + resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==} pg-protocol@1.14.0: - resolution: - { - integrity: sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==, - } + resolution: {integrity: sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==} pg-sql2@5.0.1: - resolution: - { - integrity: sha512-DdOZNUBhuBuGcq3UgUNsYE9FPdPzw9YA1K/WQxutNhC17DA/CImapyamv6lliVvdAVNZ+CWB1z+p7SmSJmkezw==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-DdOZNUBhuBuGcq3UgUNsYE9FPdPzw9YA1K/WQxutNhC17DA/CImapyamv6lliVvdAVNZ+CWB1z+p7SmSJmkezw==} + engines: {node: '>=22'} pg-types@2.2.0: - resolution: - { - integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} pg@8.20.0: - resolution: - { - integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==, - } - engines: { node: '>= 16.0.0' } + resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==} + engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' peerDependenciesMeta: @@ -13209,11 +9365,8 @@ packages: optional: true pg@8.21.0: - resolution: - { - integrity: sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==, - } - engines: { node: '>= 16.0.0' } + resolution: {integrity: sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==} + engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' peerDependenciesMeta: @@ -13221,148 +9374,85 @@ packages: optional: true pgpass@1.0.5: - resolution: - { - integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==, - } + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} pgsql-deparser@17.18.3: - resolution: - { - integrity: sha512-lD8kPWgw9KAbUbKbQKgzDGzVdtEmp25N+7qZl62I7v8Uu9Wqy7+M0EOeU96++OgPD9S1pyp9MKNGzZzPJF2C4Q==, - } + resolution: {integrity: sha512-lD8kPWgw9KAbUbKbQKgzDGzVdtEmp25N+7qZl62I7v8Uu9Wqy7+M0EOeU96++OgPD9S1pyp9MKNGzZzPJF2C4Q==} pgsql-parser@17.9.15: - resolution: - { - integrity: sha512-6+k0EtTn0CEQTR5v2APARu9En4vm46TpmpdMSfKDHkZwWZuEc08B7SeVg32VxNQ2HD5xk+dQ9TD0k9m+S+vFgg==, - } + resolution: {integrity: sha512-6+k0EtTn0CEQTR5v2APARu9En4vm46TpmpdMSfKDHkZwWZuEc08B7SeVg32VxNQ2HD5xk+dQ9TD0k9m+S+vFgg==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch-browser@2.2.6: - resolution: - { - integrity: sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w==} + engines: {node: '>=8.6'} picomatch@2.3.1: - resolution: - { - integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} picomatch@4.0.3: - resolution: - { - integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} picomatch@4.0.4: - resolution: - { - integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} pify@2.3.0: - resolution: - { - integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} pify@3.0.0: - resolution: - { - integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} pify@4.0.1: - resolution: - { - integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} pify@5.0.0: - resolution: - { - integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} pirates@4.0.7: - resolution: - { - integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} pkg-dir@4.2.0: - resolution: - { - integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} playwright-core@1.60.0: - resolution: - { - integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} + engines: {node: '>=18'} hasBin: true playwright@1.60.0: - resolution: - { - integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==} + engines: {node: '>=18'} hasBin: true pluralize@7.0.0: - resolution: - { - integrity: sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==} + engines: {node: '>=4'} postcss-selector-parser@6.1.2: - resolution: - { - integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} postcss-value-parser@4.2.0: - resolution: - { - integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, - } + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} postcss@8.5.6: - resolution: - { - integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} postgraphile@5.0.3: - resolution: - { - integrity: sha512-gO8xOusrIykV65V1rmkqnUz3ZjDU+1y09uWruUshP+t811JFsEZsE7d3UJNjAxgrBcndU0CzgmEWCbvbD3kehA==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-gO8xOusrIykV65V1rmkqnUz3ZjDU+1y09uWruUshP+t811JFsEZsE7d3UJNjAxgrBcndU0CzgmEWCbvbD3kehA==} + engines: {node: '>=22'} hasBin: true peerDependencies: '@dataplan/json': 1.0.0 @@ -13382,133 +9472,76 @@ packages: optional: true postgres-array@2.0.0: - resolution: - { - integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} postgres-array@3.0.4: - resolution: - { - integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==} + engines: {node: '>=12'} postgres-bytea@1.0.1: - resolution: - { - integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} + engines: {node: '>=0.10.0'} postgres-date@1.0.7: - resolution: - { - integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} postgres-interval@1.2.0: - resolution: - { - integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} postgres-range@1.1.4: - resolution: - { - integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==, - } + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} prettier@3.8.1: - resolution: - { - integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} hasBin: true pretty-format@26.6.2: - resolution: - { - integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} + engines: {node: '>= 10'} pretty-format@27.5.1: - resolution: - { - integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} pretty-format@29.7.0: - resolution: - { - integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} pretty-format@30.2.0: - resolution: - { - integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} pretty-format@30.4.1: - resolution: - { - integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} proc-log@4.2.0: - resolution: - { - integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} process-nextick-args@2.0.1: - resolution: - { - integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, - } + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} proggy@2.0.0: - resolution: - { - integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} promise-all-reject-late@1.0.1: - resolution: - { - integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==, - } + resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} promise-call-limit@3.0.2: - resolution: - { - integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==, - } + resolution: {integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==} promise-inflight@1.0.1: - resolution: - { - integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==, - } + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} peerDependencies: bluebird: '*' peerDependenciesMeta: @@ -13516,164 +9549,92 @@ packages: optional: true promise-retry@2.0.1: - resolution: - { - integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} promzard@1.0.2: - resolution: - { - integrity: sha512-2FPputGL+mP3jJ3UZg/Dl9YOkovB7DX0oOr+ck5QbZ5MtORtds8k/BZdn+02peDLI8/YWbmzx34k5fA+fHvCVQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-2FPputGL+mP3jJ3UZg/Dl9YOkovB7DX0oOr+ck5QbZ5MtORtds8k/BZdn+02peDLI8/YWbmzx34k5fA+fHvCVQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} proto-list@1.2.4: - resolution: - { - integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==, - } + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} protocols@2.0.2: - resolution: - { - integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==, - } + resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} proxy-addr@2.0.7: - resolution: - { - integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} proxy-from-env@1.1.0: - resolution: - { - integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, - } + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} pstree.remy@1.1.8: - resolution: - { - integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==, - } + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} punycode.js@2.3.1: - resolution: - { - integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} pure-rand@7.0.1: - resolution: - { - integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==, - } + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} qs@6.14.0: - resolution: - { - integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} qs@6.14.1: - resolution: - { - integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + engines: {node: '>=0.6'} queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} quick-lru@4.0.1: - resolution: - { - integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} range-parser@1.2.1: - resolution: - { - integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} raw-body@3.0.2: - resolution: - { - integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} react-compiler-runtime@19.1.0-rc.1: - resolution: - { - integrity: sha512-wCt6g+cRh8g32QT18/9blfQHywGjYu+4FlEc3CW1mx3pPxYzZZl1y+VtqxRgnKKBCFLIGUYxog4j4rs5YS86hw==, - } + resolution: {integrity: sha512-wCt6g+cRh8g32QT18/9blfQHywGjYu+4FlEc3CW1mx3pPxYzZZl1y+VtqxRgnKKBCFLIGUYxog4j4rs5YS86hw==} peerDependencies: react: ^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental react-dom@19.2.4: - resolution: - { - integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==, - } + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: react: ^19.2.4 react-dom@19.2.5: - resolution: - { - integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==, - } + resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} peerDependencies: react: ^19.2.5 react-is@16.13.1: - resolution: - { - integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, - } + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} react-is@17.0.2: - resolution: - { - integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, - } + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} react-is@18.3.1: - resolution: - { - integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==, - } + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} react-is@19.2.6: - resolution: - { - integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==, - } + resolution: {integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==} react-query@3.39.3: - resolution: - { - integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==, - } + resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: '*' @@ -13685,18 +9646,12 @@ packages: optional: true react-refresh@0.17.0: - resolution: - { - integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} react-remove-scroll-bar@2.3.8: - resolution: - { - integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -13705,11 +9660,8 @@ packages: optional: true react-remove-scroll@2.7.2: - resolution: - { - integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -13718,11 +9670,8 @@ packages: optional: true react-style-singleton@2.2.3: - resolution: - { - integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -13731,260 +9680,149 @@ packages: optional: true react-test-renderer@19.2.6: - resolution: - { - integrity: sha512-GbS6V23YduFTPiWJ5xICbKEjRcqx1Z90js/V5miqhz7qp/d6xSe9Dd6NjSQODFRdzdsqRMPW82E/sFpPRbY5Mw==, - } + resolution: {integrity: sha512-GbS6V23YduFTPiWJ5xICbKEjRcqx1Z90js/V5miqhz7qp/d6xSe9Dd6NjSQODFRdzdsqRMPW82E/sFpPRbY5Mw==} peerDependencies: react: ^19.2.6 react@19.2.4: - resolution: - { - integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} react@19.2.5: - resolution: - { - integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} + engines: {node: '>=0.10.0'} read-cmd-shim@4.0.0: - resolution: - { - integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} read-package-json-fast@3.0.2: - resolution: - { - integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} read-pkg-up@3.0.0: - resolution: - { - integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} + engines: {node: '>=4'} read-pkg-up@7.0.1: - resolution: - { - integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} read-pkg@3.0.0: - resolution: - { - integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} read-pkg@5.2.0: - resolution: - { - integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} read@3.0.1: - resolution: - { - integrity: sha512-SLBrDU/Srs/9EoWhU5GdbAoxG1GzpQHo/6qiGItaoLJ1thmYpcNIM1qISEUvyHBzfGlWIyd6p2DNi1oV1VmAuw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-SLBrDU/Srs/9EoWhU5GdbAoxG1GzpQHo/6qiGItaoLJ1thmYpcNIM1qISEUvyHBzfGlWIyd6p2DNi1oV1VmAuw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} readable-stream@1.0.34: - resolution: - { - integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==, - } + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} readable-stream@2.3.8: - resolution: - { - integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, - } + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} readable-stream@3.6.2: - resolution: - { - integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} readdirp@3.6.0: - resolution: - { - integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, - } - engines: { node: '>=8.10.0' } + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} redent@3.0.0: - resolution: - { - integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} regenerator-runtime@0.10.5: - resolution: - { - integrity: sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==, - } + resolution: {integrity: sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==} relateurl@0.2.7: - resolution: - { - integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} remove-accents@0.5.0: - resolution: - { - integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==, - } + resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} request-ip@3.3.0: - resolution: - { - integrity: sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==, - } + resolution: {integrity: sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==} require-directory@2.1.1: - resolution: - { - integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} require-from-string@2.0.2: - resolution: - { - integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} require-main-filename@2.0.0: - resolution: - { - integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==, - } + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} requires-port@1.0.0: - resolution: - { - integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==, - } + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} resolve-cwd@3.0.0: - resolution: - { - integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} resolve-from@5.0.0: - resolution: - { - integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} resolve.exports@2.0.3: - resolution: - { - integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} resolve@1.22.11: - resolution: - { - integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} hasBin: true restore-cursor@3.1.0: - resolution: - { - integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} retry@0.12.0: - resolution: - { - integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} retry@0.13.1: - resolution: - { - integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} reusify@1.1.0: - resolution: - { - integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, - } - engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rimraf@3.0.2: - resolution: - { - integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==, - } + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@4.4.1: - resolution: - { - integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} hasBin: true rimraf@6.1.3: - resolution: - { - integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} + engines: {node: 20 || >=22} hasBin: true rollup-plugin-visualizer@6.0.5: - resolution: - { - integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==} + engines: {node: '>=18'} hasBin: true peerDependencies: rolldown: 1.x || ^1.0.0-beta @@ -13996,45 +9834,27 @@ packages: optional: true rollup@4.57.1: - resolution: - { - integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==, - } - engines: { node: '>=18.0.0', npm: '>=8.0.0' } + resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true router@2.2.0: - resolution: - { - integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} rrweb-cssom@0.8.0: - resolution: - { - integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==, - } + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} run-async@2.4.1: - resolution: - { - integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==, - } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} ruru-types@2.0.0: - resolution: - { - integrity: sha512-7dBZHeU8Pnj0V+tLiPzr8RhpdsNuAwu5yhZqcolu6pzpItLG/LKKzN+gKAiCp17z6Lfpdu7bXs+9JS39PO+VxA==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-7dBZHeU8Pnj0V+tLiPzr8RhpdsNuAwu5yhZqcolu6pzpItLG/LKKzN+gKAiCp17z6Lfpdu7bXs+9JS39PO+VxA==} + engines: {node: '>=22'} peerDependencies: graphql: 16.13.0 react: ^18 || ^19 @@ -14046,11 +9866,8 @@ packages: optional: true ruru@2.0.0: - resolution: - { - integrity: sha512-I8N4Jw0jsgFqgUnsLMR9BHnWyVX0xj7GfDYIjsvjt538zIVs/PiggdepsYjH6K2ul9bjHoS15p7XL2SnywSdCw==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-I8N4Jw0jsgFqgUnsLMR9BHnWyVX0xj7GfDYIjsvjt538zIVs/PiggdepsYjH6K2ul9bjHoS15p7XL2SnywSdCw==} + engines: {node: '>=22'} hasBin: true peerDependencies: graphile-config: ^1.0.0-rc.5 @@ -14064,751 +9881,421 @@ packages: optional: true rxjs@7.8.2: - resolution: - { - integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==, - } + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safe-buffer@5.1.2: - resolution: - { - integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, - } + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: - resolution: - { - integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, - } + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} safer-buffer@2.1.2: - resolution: - { - integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, - } + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} saxes@6.0.0: - resolution: - { - integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, - } - engines: { node: '>=v12.22.7' } + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} scheduler@0.27.0: - resolution: - { - integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, - } + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} schema-typescript@0.14.3: - resolution: - { - integrity: sha512-DvagD66tMo1rDmvCQKZvBNCadk54vGJF/glzBvS4M57IRCOSpbczWHpq0dwEcLpl+EbsIusK4cUsL+RKXVaeYA==, - } + resolution: {integrity: sha512-DvagD66tMo1rDmvCQKZvBNCadk54vGJF/glzBvS4M57IRCOSpbczWHpq0dwEcLpl+EbsIusK4cUsL+RKXVaeYA==} semver@5.7.2: - resolution: - { - integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, - } + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.7.3: - resolution: - { - integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} hasBin: true semver@7.7.4: - resolution: - { - integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} hasBin: true semver@7.8.1: - resolution: - { - integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} hasBin: true send@1.2.1: - resolution: - { - integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} serve-static@2.2.1: - resolution: - { - integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} set-blocking@2.0.0: - resolution: - { - integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==, - } + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} set-value@4.1.0: - resolution: - { - integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==, - } - engines: { node: '>=11.0' } + resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==} + engines: {node: '>=11.0'} setprototypeof@1.2.0: - resolution: - { - integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==, - } + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} shallow-clone@3.0.1: - resolution: - { - integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} shallowequal@1.1.0: - resolution: - { - integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==, - } + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} shelljs@0.10.0: - resolution: - { - integrity: sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==} + engines: {node: '>=18'} side-channel-list@1.0.0: - resolution: - { - integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} side-channel-map@1.0.1: - resolution: - { - integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} side-channel-weakmap@1.0.2: - resolution: - { - integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} side-channel@1.1.0: - resolution: - { - integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} signal-exit@3.0.7: - resolution: - { - integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, - } + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} signal-exit@4.1.0: - resolution: - { - integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} sigstore@2.3.1: - resolution: - { - integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==} + engines: {node: ^16.14.0 || >=18.0.0} simple-update-notifier@2.0.0: - resolution: - { - integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} slash@3.0.0: - resolution: - { - integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} slick@1.12.2: - resolution: - { - integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==, - } + resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} smart-buffer@4.2.0: - resolution: - { - integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==, - } - engines: { node: '>= 6.0.0', npm: '>= 3.0.0' } + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} smtp-server@3.18.4: - resolution: - { - integrity: sha512-9EnXPG4Tv+2P/TSEUdFTduYn9IxtxNRsOq/ryVj8ZlT+6MU2um9gn2Td2hHlgH1n+saagMWtici3hn5J5PhU+g==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-9EnXPG4Tv+2P/TSEUdFTduYn9IxtxNRsOq/ryVj8ZlT+6MU2um9gn2Td2hHlgH1n+saagMWtici3hn5J5PhU+g==} + engines: {node: '>=18.18.0'} socks-proxy-agent@8.0.5: - resolution: - { - integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} socks@2.8.7: - resolution: - { - integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==, - } - engines: { node: '>= 10.0.0', npm: '>= 3.0.0' } + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} sort-keys@2.0.0: - resolution: - { - integrity: sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==} + engines: {node: '>=4'} sorted-array-functions@1.3.0: - resolution: - { - integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==, - } + resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==} source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} source-map-resolve@0.6.0: - resolution: - { - integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==, - } + resolution: {integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==} deprecated: See https://github.com/lydell/source-map-resolve#deprecated source-map-support@0.5.13: - resolution: - { - integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==, - } + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} source-map@0.6.1: - resolution: - { - integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} source-map@0.7.6: - resolution: - { - integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==, - } - engines: { node: '>= 12' } + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} spdx-correct@3.2.0: - resolution: - { - integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, - } + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} spdx-exceptions@2.5.0: - resolution: - { - integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==, - } + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} spdx-expression-parse@3.0.1: - resolution: - { - integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, - } + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} spdx-license-ids@3.0.22: - resolution: - { - integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==, - } + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} split2@3.2.2: - resolution: - { - integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==, - } + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} split2@4.2.0: - resolution: - { - integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==, - } - engines: { node: '>= 10.x' } + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} split@1.0.1: - resolution: - { - integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==, - } + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} sprintf-js@1.0.3: - resolution: - { - integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==, - } + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} ssri@10.0.6: - resolution: - { - integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} stack-utils@2.0.6: - resolution: - { - integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} statuses@1.5.0: - resolution: - { - integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} statuses@2.0.2: - resolution: - { - integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} stream-browserify@3.0.0: - resolution: - { - integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==, - } + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} streamsearch@0.1.2: - resolution: - { - integrity: sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==} + engines: {node: '>=0.8.0'} strfy-js@3.2.2: - resolution: - { - integrity: sha512-hUgJ5k2PR1ivhq4uObxnin5j6GcOr0Y0N1lzi3z6SRhxNqu4rzpDfyoC2ToUAyM8yXNXM0zs6f4KIiqj8NqheQ==, - } + resolution: {integrity: sha512-hUgJ5k2PR1ivhq4uObxnin5j6GcOr0Y0N1lzi3z6SRhxNqu4rzpDfyoC2ToUAyM8yXNXM0zs6f4KIiqj8NqheQ==} string-length@4.0.2: - resolution: - { - integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} string-width@4.2.3: - resolution: - { - integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} string-width@5.1.2: - resolution: - { - integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} string_decoder@0.10.31: - resolution: - { - integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==, - } + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} string_decoder@1.1.1: - resolution: - { - integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, - } + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} string_decoder@1.3.0: - resolution: - { - integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, - } + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} strip-ansi@6.0.1: - resolution: - { - integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} strip-ansi@7.1.2: - resolution: - { - integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} strip-bom@3.0.0: - resolution: - { - integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} strip-bom@4.0.0: - resolution: - { - integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} strip-final-newline@2.0.0: - resolution: - { - integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} strip-indent@3.0.0: - resolution: - { - integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} strip-json-comments@3.1.1: - resolution: - { - integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} strnum@2.3.0: - resolution: - { - integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==, - } + resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} styled-components@5.3.11: - resolution: - { - integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} + engines: {node: '>=10'} peerDependencies: react: '>= 16.8.0' react-dom: '>= 16.8.0' react-is: '>= 16.8.0' styled-system@5.1.5: - resolution: - { - integrity: sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==, - } + resolution: {integrity: sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==} superagent@10.3.0: - resolution: - { - integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==, - } - engines: { node: '>=14.18.0' } + resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} + engines: {node: '>=14.18.0'} supertest@7.2.2: - resolution: - { - integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==, - } - engines: { node: '>=14.18.0' } + resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} + engines: {node: '>=14.18.0'} supports-color@5.5.0: - resolution: - { - integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} supports-color@7.2.0: - resolution: - { - integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} supports-color@8.1.1: - resolution: - { - integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} supports-preserve-symlinks-flag@1.0.0: - resolution: - { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} symbol-tree@3.2.4: - resolution: - { - integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, - } + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} synckit@0.11.12: - resolution: - { - integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==, - } - engines: { node: ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + engines: {node: ^14.18.0 || >=16.0.0} tabbable@6.4.0: - resolution: - { - integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==, - } + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} tamedevil@0.1.1: - resolution: - { - integrity: sha512-YH5/T/FXUYrsfFSsCdLqJwUGAlbTBrK2V78dftXnOIgnOnM9aYBi3C+uUg9pevezjE2ENPyOxHqnXJrTG9WPFQ==, - } - engines: { node: '>=22' } + resolution: {integrity: sha512-YH5/T/FXUYrsfFSsCdLqJwUGAlbTBrK2V78dftXnOIgnOnM9aYBi3C+uUg9pevezjE2ENPyOxHqnXJrTG9WPFQ==} + engines: {node: '>=22'} tar-stream@2.2.0: - resolution: - { - integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} tar@6.2.1: - resolution: - { - integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} 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 temp-dir@1.0.0: - resolution: - { - integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==} + engines: {node: '>=4'} test-exclude@6.0.0: - resolution: - { - integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} text-extensions@1.9.0: - resolution: - { - integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} through2@2.0.5: - resolution: - { - integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==, - } + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} through@2.3.8: - resolution: - { - integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, - } + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} tinyglobby@0.2.12: - resolution: - { - integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} tinyglobby@0.2.15: - resolution: - { - integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} tinyglobby@0.2.16: - resolution: - { - integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} tinypool@2.1.0: - resolution: - { - integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==, - } - engines: { node: ^20.0.0 || >=22.0.0 } + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} tldts-core@6.1.86: - resolution: - { - integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==, - } + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} tldts@6.1.86: - resolution: - { - integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==, - } + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true tmp@0.2.5: - resolution: - { - integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==, - } - engines: { node: '>=14.14' } + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} tmpl@1.0.5: - resolution: - { - integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==, - } + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: '>=8.0' } + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} toidentifier@1.0.1: - resolution: - { - integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} touch@3.1.1: - resolution: - { - integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==, - } + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} hasBin: true tough-cookie@5.1.2: - resolution: - { - integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} tr46@0.0.3: - resolution: - { - integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, - } + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} tr46@5.1.1: - resolution: - { - integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} transliteration@2.6.1: - resolution: - { - integrity: sha512-hJ9BhrQAOnNTbpOr1MxsNjZISkn7ppvF5TKUeFmTE1mG4ZPD/XVxF0L0LUoIUCWmQyxH0gJpVtfYLAWf298U9w==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-hJ9BhrQAOnNTbpOr1MxsNjZISkn7ppvF5TKUeFmTE1mG4ZPD/XVxF0L0LUoIUCWmQyxH0gJpVtfYLAWf298U9w==} + engines: {node: '>=20.0.0'} hasBin: true treeverse@3.0.0: - resolution: - { - integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} trim-newlines@3.0.1: - resolution: - { - integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} ts-api-utils@2.5.0: - resolution: - { - integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, - } - engines: { node: '>=18.12' } + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' ts-jest@29.4.11: - resolution: - { - integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==, - } - engines: { node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0 } + resolution: {integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@babel/core': '>=7.0.0-beta.0 <8' @@ -14834,10 +10321,7 @@ packages: optional: true ts-node@10.9.2: - resolution: - { - integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==, - } + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: '@swc/core': '>=1.2.50' @@ -14851,257 +10335,146 @@ packages: optional: true tsconfig-paths@4.2.0: - resolution: - { - integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tsx@4.21.0: - resolution: - { - integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} hasBin: true tuf-js@2.2.1: - resolution: - { - integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==} + engines: {node: ^16.14.0 || >=18.0.0} type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} type-detect@4.0.8: - resolution: - { - integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} type-fest@0.18.1: - resolution: - { - integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} type-fest@0.21.3: - resolution: - { - integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} type-fest@0.4.1: - resolution: - { - integrity: sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==} + engines: {node: '>=6'} type-fest@0.6.0: - resolution: - { - integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} type-fest@0.8.1: - resolution: - { - integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} type-fest@4.41.0: - resolution: - { - integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} type-is@2.0.1: - resolution: - { - integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} typedarray@0.0.6: - resolution: - { - integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==, - } + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} typescript@5.9.3: - resolution: - { - integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, - } - engines: { node: '>=14.17' } + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true uc.micro@2.1.0: - resolution: - { - integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==, - } + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} uglify-js@3.19.3: - resolution: - { - integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} hasBin: true uglify-js@3.4.10: - resolution: - { - integrity: sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==} + engines: {node: '>=0.8.0'} hasBin: true undefsafe@2.0.5: - resolution: - { - integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==, - } + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} undici-types@6.21.0: - resolution: - { - integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, - } + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} undici-types@7.24.6: - resolution: - { - integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==, - } + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} undici@7.25.0: - resolution: - { - integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==, - } - engines: { node: '>=20.18.1' } + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} + engines: {node: '>=20.18.1'} undici@8.3.0: - resolution: - { - integrity: sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==, - } - engines: { node: '>=22.19.0' } + resolution: {integrity: sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==} + engines: {node: '>=22.19.0'} unique-filename@3.0.0: - resolution: - { - integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} unique-slug@4.0.0: - resolution: - { - integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} universal-user-agent@6.0.1: - resolution: - { - integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==, - } + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} universalify@2.0.1: - resolution: - { - integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==, - } - engines: { node: '>= 10.0.0' } + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} unload@2.2.0: - resolution: - { - integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==, - } + resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} unpipe@1.0.0: - resolution: - { - integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} unrs-resolver@1.12.2: - resolution: - { - integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==, - } + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} untildify@4.0.0: - resolution: - { - integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} upath@2.0.1: - resolution: - { - integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} + engines: {node: '>=4'} update-browserslist-db@1.2.3: - resolution: - { - integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==, - } + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' upper-case@1.1.3: - resolution: - { - integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==, - } + resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} uri-js@4.4.1: - resolution: - { - integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, - } + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} url-join@4.0.1: - resolution: - { - integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==, - } + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} use-callback-ref@1.3.3: - resolution: - { - integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -15110,11 +10483,8 @@ packages: optional: true use-sidecar@1.1.3: - resolution: - { - integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -15123,73 +10493,43 @@ packages: optional: true use-sync-external-store@1.6.0: - resolution: - { - integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==, - } + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 util-deprecate@1.0.2: - resolution: - { - integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, - } + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} uuid@10.0.0: - resolution: - { - integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==, - } + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true v8-compile-cache-lib@3.0.1: - resolution: - { - integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==, - } + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} v8-to-istanbul@9.3.0: - resolution: - { - integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==, - } - engines: { node: '>=10.12.0' } + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} valid-data-url@3.0.1: - resolution: - { - integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} + engines: {node: '>=10'} validate-npm-package-license@3.0.4: - resolution: - { - integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, - } + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} validate-npm-package-name@5.0.1: - resolution: - { - integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} vary@1.1.2: - resolution: - { - integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} vite@6.4.1: - resolution: - { - integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==, - } - engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 @@ -15228,191 +10568,107 @@ packages: optional: true vscode-languageserver-types@3.17.5: - resolution: - { - integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==, - } + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} w3c-xmlserializer@5.0.0: - resolution: - { - integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} walk-up-path@3.0.1: - resolution: - { - integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==, - } + resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} walker@1.0.8: - resolution: - { - integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, - } + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} warning@3.0.0: - resolution: - { - integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==, - } + resolution: {integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==} wcwidth@1.0.1: - resolution: - { - integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==, - } + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} web-resource-inliner@5.0.0: - resolution: - { - integrity: sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A==, - } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A==} + engines: {node: '>=10.0.0'} webidl-conversions@3.0.1: - resolution: - { - integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==, - } + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} webidl-conversions@7.0.0: - resolution: - { - integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} whatwg-encoding@3.1.1: - resolution: - { - integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@4.0.0: - resolution: - { - integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} whatwg-url@14.2.0: - resolution: - { - integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} whatwg-url@5.0.0: - resolution: - { - integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, - } + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} which-module@2.0.1: - resolution: - { - integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==, - } + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} hasBin: true which@4.0.0: - resolution: - { - integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==, - } - engines: { node: ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} hasBin: true wide-align@1.1.5: - resolution: - { - integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==, - } + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} word-wrap@1.2.5: - resolution: - { - integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} wordwrap@1.0.0: - resolution: - { - integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==, - } + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} wrap-ansi@6.2.0: - resolution: - { - integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} wrap-ansi@7.0.0: - resolution: - { - integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} wrap-ansi@8.1.0: - resolution: - { - integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} wrappy@1.0.2: - resolution: - { - integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, - } + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} write-file-atomic@2.4.3: - resolution: - { - integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==, - } + resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==} write-file-atomic@5.0.1: - resolution: - { - integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} write-json-file@3.2.0: - resolution: - { - integrity: sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==} + engines: {node: '>=6'} write-pkg@4.0.0: - resolution: - { - integrity: sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==} + engines: {node: '>=8'} ws@8.20.1: - resolution: - { - integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==, - } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: '>=5.0.2' @@ -15423,133 +10679,76 @@ packages: optional: true xml-name-validator@5.0.0: - resolution: - { - integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} xml-naming@0.1.0: - resolution: - { - integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} + engines: {node: '>=16.0.0'} xmlchars@2.2.0: - resolution: - { - integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, - } + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} xtend@4.0.2: - resolution: - { - integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, - } - engines: { node: '>=0.4' } + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} y18n@4.0.3: - resolution: - { - integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==, - } + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} y18n@5.0.8: - resolution: - { - integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yallist@4.0.0: - resolution: - { - integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, - } + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} yaml@2.8.4: - resolution: - { - integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==, - } - engines: { node: '>= 14.6' } + resolution: {integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==} + engines: {node: '>= 14.6'} hasBin: true yanse@0.2.1: - resolution: - { - integrity: sha512-SMi3ZO1IqsvPLLXuy8LBCP1orqcjOT8VygiuyAlplaGeH2g+n4ZSSyWlA/BZjuUuN58TyOcz89mVkflSqIPxxQ==, - } + resolution: {integrity: sha512-SMi3ZO1IqsvPLLXuy8LBCP1orqcjOT8VygiuyAlplaGeH2g+n4ZSSyWlA/BZjuUuN58TyOcz89mVkflSqIPxxQ==} yargs-parser@18.1.3: - resolution: - { - integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} yargs-parser@20.2.9: - resolution: - { - integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} yargs-parser@21.1.1: - resolution: - { - integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} yargs@15.4.1: - resolution: - { - integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} yargs@16.2.0: - resolution: - { - integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} yargs@17.7.2: - resolution: - { - integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} yn@3.1.1: - resolution: - { - integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} zustand@5.0.11: - resolution: - { - integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==, - } - engines: { node: '>=12.20.0' } + resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} + engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' immer: '>=9.0.6' @@ -15566,6 +10765,7 @@ packages: optional: true snapshots: + '@0no-co/graphql.web@1.2.0(graphql@16.13.0)': optionalDependencies: graphql: 16.13.0 @@ -15862,12 +11062,6 @@ snapshots: '@aws/lambda-invoke-store@0.2.4': {} - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - '@babel/code-frame@7.28.6': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -16098,7 +11292,7 @@ snapshots: '@babel/template@7.28.6': dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.28.6 '@babel/parser': 7.29.0 '@babel/types': 7.29.0 @@ -16116,7 +11310,7 @@ snapshots: '@babel/traverse@7.28.6': dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.28.6 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 '@babel/parser': 7.29.0 @@ -18296,7 +13490,7 @@ snapshots: '@testing-library/dom@7.31.2': dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.0 '@babel/runtime': 7.28.4 '@types/aria-query': 4.2.2 aria-query: 4.2.2 @@ -22679,7 +17873,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.28.6 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4