From b010e179e699352f91da6504cf24d5bbd8e66f3b Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 21 Apr 2026 07:22:42 +0800
Subject: [PATCH 01/17] the introspection cache and buildkey solution
---
.gitignore | 1 +
.../graphile-multi-tenancy-cache/README.md | 24 +
graphile/graphile-multi-tenancy-cache/SPEC.md | 480 +
.../jest.config.js | 18 +
.../graphile-multi-tenancy-cache/package.json | 58 +
.../src/__tests__/buildkey.test.ts | 1098 ++
.../src/__tests__/fingerprint.test.ts | 125 +
.../src/__tests__/introspection-cache.test.ts | 87 +
.../graphile-multi-tenancy-cache/src/index.ts | 38 +
.../src/introspection-cache.ts | 232 +
.../src/multi-tenancy-cache.ts | 476 +
.../src/utils/fingerprint.ts | 175 +
.../src/utils/introspection-query.ts | 124 +
.../tsconfig.esm.json | 7 +
.../tsconfig.json | 10 +
graphql/env/src/env.ts | 2 +
graphql/server/package.json | 1 +
graphql/server/perf/E2E_BENCHMARK_REPORT.md | 155 +
graphql/server/perf/README.md | 125 +
.../perf/build-business-op-profiles.mjs | 483 +
.../server/perf/build-keyspace-profiles.mjs | 333 +
graphql/server/perf/build-token-pool.mjs | 161 +
graphql/server/perf/common.mjs | 231 +
graphql/server/perf/e2e-benchmark.ts | 398 +
graphql/server/perf/phase1-preflight.mjs | 761 ++
.../server/perf/phase1-tech-validate-dbpm.mjs | 768 ++
graphql/server/perf/phase2-load.mjs | 1414 +++
.../perf/prepare-public-test-access.mjs | 129 +
.../server/perf/public-test-access-lib.mjs | 158 +
.../server/perf/reset-business-test-data.mjs | 187 +
graphql/server/perf/run-comparison.sh | 245 +
graphql/server/perf/run-e2e-benchmark.sh | 111 +
graphql/server/perf/run-k-sweep.mjs | 713 ++
graphql/server/perf/run-stress-suite.sh | 143 +
graphql/server/perf/run-test-spec.mjs | 61 +
graphql/server/perf/seed-real-multitenant.mjs | 312 +
graphql/server/perf/summarize-shapes.mjs | 222 +
graphql/server/src/index.ts | 4 +-
graphql/server/src/middleware/flush.ts | 43 +
graphql/server/src/middleware/graphile.ts | 97 +
graphql/server/src/server.ts | 19 +-
graphql/types/src/graphile.ts | 2 +
pnpm-lock.yaml | 10278 +++++-----------
43 files changed, 13056 insertions(+), 7453 deletions(-)
create mode 100644 graphile/graphile-multi-tenancy-cache/README.md
create mode 100644 graphile/graphile-multi-tenancy-cache/SPEC.md
create mode 100644 graphile/graphile-multi-tenancy-cache/jest.config.js
create mode 100644 graphile/graphile-multi-tenancy-cache/package.json
create mode 100644 graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/__tests__/fingerprint.test.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/index.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/tsconfig.esm.json
create mode 100644 graphile/graphile-multi-tenancy-cache/tsconfig.json
create mode 100644 graphql/server/perf/E2E_BENCHMARK_REPORT.md
create mode 100644 graphql/server/perf/README.md
create mode 100644 graphql/server/perf/build-business-op-profiles.mjs
create mode 100644 graphql/server/perf/build-keyspace-profiles.mjs
create mode 100644 graphql/server/perf/build-token-pool.mjs
create mode 100644 graphql/server/perf/common.mjs
create mode 100644 graphql/server/perf/e2e-benchmark.ts
create mode 100644 graphql/server/perf/phase1-preflight.mjs
create mode 100644 graphql/server/perf/phase1-tech-validate-dbpm.mjs
create mode 100644 graphql/server/perf/phase2-load.mjs
create mode 100644 graphql/server/perf/prepare-public-test-access.mjs
create mode 100644 graphql/server/perf/public-test-access-lib.mjs
create mode 100644 graphql/server/perf/reset-business-test-data.mjs
create mode 100755 graphql/server/perf/run-comparison.sh
create mode 100755 graphql/server/perf/run-e2e-benchmark.sh
create mode 100644 graphql/server/perf/run-k-sweep.mjs
create mode 100644 graphql/server/perf/run-stress-suite.sh
create mode 100644 graphql/server/perf/run-test-spec.mjs
create mode 100644 graphql/server/perf/seed-real-multitenant.mjs
create mode 100644 graphql/server/perf/summarize-shapes.mjs
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..83b0a4a5ab
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/README.md
@@ -0,0 +1,24 @@
+# graphile-multi-tenancy-cache
+
+Template-based multi-tenancy optimization for PostGraphile v5. Shares PostGraphile instances across tenants with identical schema structures using AST-based SQL rewriting.
+
+## Features
+
+- **Schema fingerprinting** — structural comparison (tables, columns, constraints) ignoring schema names
+- **Template sharing** — one PostGraphile instance per unique fingerprint, shared across N tenants
+- **AST-based SQL rewrite** — safe schema remapping via `pgsql-parser`/`pgsql-deparser`
+- **Three cache layers** — introspection cache, template registry, SQL rewrite cache (all LRU + TTL)
+- **No Crystal fork** — wrapper plugin intercepts at `withPgClient` level via Grafast middleware
+
+## Architecture
+
+```
+Request → authenticate → resolve tenant schemas
+ → introspection cache (fingerprint lookup)
+ → template registry (shared PostGraphile instance)
+ → PgMultiTenancyWrapperPlugin (client.query Proxy)
+ → AST rewrite cache (schema name remapping)
+ → PostgreSQL
+```
+
+See [SPEC.md](./SPEC.md) for full architecture documentation.
diff --git a/graphile/graphile-multi-tenancy-cache/SPEC.md b/graphile/graphile-multi-tenancy-cache/SPEC.md
new file mode 100644
index 0000000000..a2d09348fd
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/SPEC.md
@@ -0,0 +1,480 @@
+# graphile-multi-tenancy-cache — Implementation Spec
+
+## Problem
+
+Constructive's GraphQL server creates a **dedicated PostGraphile instance per tenant** (one `postgraphile()` call per unique `svc_key`). Each instance holds its own `PgRegistry`, `GraphQLSchema`, operation plan cache, and V8 closures — ~50–80 MB per tenant. At scale (hundreds of tenants), this leads to:
+
+- **Unbounded memory growth** — RSS grows linearly with tenant count
+- **Slow cold starts** — each new tenant triggers a full schema build (~200–400ms)
+- **LRU churn** — when tenant count exceeds `GRAPHILE_CACHE_MAX`, constant eviction/rebuild cycles tank QPS and spike latency
+
+## Solution
+
+A **template-based multi-tenancy cache** that shares a single PostGraphile instance across all tenants with structurally identical schemas. SQL is remapped per-request at the `client.query()` level — no Crystal modifications required.
+
+### Key invariant
+
+Constructive tenant schemas use the naming convention `t__` (e.g., `t_1_services_public`, `t_2_services_public`). These names **never collide** with table/column names (`apis`, `apps`, `domains`). This invariant is necessary but not sufficient for safety, so SQL remap uses AST-based rewrite with strict failure policy (see SQL Remap Safety Contract below), not best-effort raw text replacement.
+
+---
+
+## Architecture
+
+### Request flow
+
+```
+Request (svc_key)
+ │
+ ├─ HIT ──► tenantInstances.get(svc_key) ──► inject sqlTextTransform ──► handler
+ │
+ └─ MISS ──► getOrCreateTenantInstance()
+ │
+ ├─ Introspect + fingerprint (cached)
+ │
+ ├─ Template exists for fingerprint?
+ │ ├─ YES ──► reuse template, build schema remap transform
+ │ └─ NO ──► build new template (single-flight), register
+ │
+ └─ Return TenantInstance { handler, sqlTextTransform, pgSettings }
+```
+
+### SQL interception (wrapper approach)
+
+```
+PgContextPlugin (Crystal, runs first in prepareArgs)
+ contextValue["withPgClient"] = withPgClientFromPgService(…)
+ │
+PgMultiTenancyWrapperPlugin (this package, runs AFTER)
+ contextValue["withPgClient"] = wrap(original, contextValue)
+ │
+PgExecutor reads ctx.get("withPgClient") at execution time
+ → gets wrapped version
+ → client.query({ text }) passes through Proxy
+ → SQL text transformed: "t_1_services_public" → "t_2_services_public"
+ → PostgreSQL receives tenant-correct SQL
+```
+
+The transform is read **lazily** at call time (not at middleware time) because `grafast.context` finalization happens after middleware.
+
+### Three cache layers
+
+| Layer | Key | Value | Eviction |
+|---|---|---|---|
+| **Tenant Instance** | `svc_key` | `TenantInstance` (handler + transform) | Package-owned: `flushTenantInstance()`, flush via LISTEN/NOTIFY |
+| **Introspection** | `connHash:schema1,schema2` | Parsed introspection + fingerprint | LRU (max 100) + TTL (30min idle) |
+| **Template** | SHA-256 fingerprint | PostGraphile instance (pgl + handler + httpServer) | LRU (max 50) + TTL (30min idle) + refCount protection |
+
+---
+
+## Folder structure
+
+### New package: `graphile/graphile-multi-tenancy-cache/`
+
+```
+graphile/graphile-multi-tenancy-cache/
+├── SPEC.md ← this file
+├── package.json
+├── jest.config.js
+├── tsconfig.json
+├── tsconfig.esm.json
+└── src/
+ ├── index.ts ← public API exports
+ │
+ │ # Core modules
+ ├── pg-client-wrapper-plugin.ts ← Grafast middleware (SQL interception via Proxy)
+ ├── multi-tenancy-cache.ts ← orchestrator (full lifecycle: tenant instances, templates, shutdown)
+ ├── registry-template-map.ts ← template registry (LRU/TTL eviction, refCount)
+ ├── introspection-cache.ts ← introspection cache (LRU/TTL eviction)
+ │
+ │ # Utilities
+ ├── utils/
+ │ ├── fingerprint.ts ← SHA-256 structural fingerprint (schema-name-agnostic)
+ │ ├── sql-transform.ts ← SQL remap orchestrator (AST rewrite + hot-path cache)
+ │ ├── schema-map.ts ← buildSchemaMap, buildTenantPgSettings, remapSchemas
+ │ └── introspection-query.ts ← fetchIntrospection, parseIntrospection (raw pg_catalog access)
+ │
+ │ # Tests
+ └── __tests__/
+ ├── pg-client-wrapper-plugin.test.ts
+ ├── registry-template-map.test.ts
+ ├── introspection-cache.test.ts
+ ├── fingerprint.test.ts
+ ├── sql-transform.test.ts
+ ├── schema-map.test.ts
+ └── single-flight.test.ts
+```
+
+### Modified files in existing packages
+
+```
+graphql/server/src/middleware/
+├── graphile.ts ← add multiTenancyHandler (calls package APIs, no preset builder)
+└── flush.ts ← add multi-tenancy cache invalidation (calls package's flushTenantInstance)
+
+graphql/server/src/
+├── server.ts ← wire shutdownMultiTenancyCache, createFlushMiddleware
+└── index.ts ← export createFlushMiddleware
+
+graphql/env/src/
+└── env.ts ← add USE_MULTI_TENANCY_CACHE env var
+
+graphql/types/src/
+└── graphile.ts ← add useMultiTenancyCache to ApiOptions
+```
+
+### Benchmark scripts: `graphql/server/perf/`
+
+E2E benchmark scripts live at the server level (not in the package) since they
+start the actual GraphQL server, manage databases, and do HTTP load testing.
+
+```
+graphql/server/perf/
+├── README.md ← usage docs
+├── common.mjs ← shared utilities (fetch, timing, pool helpers)
+├── run-k-sweep.mjs ← orchestrator: run both modes, compare results
+├── run-test-spec.mjs ← single-mode runner (dedicated or multi-tenant)
+├── phase1-preflight.mjs ← pre-flight checks (DB connectivity, server health)
+├── phase1-tech-validate-dbpm.mjs ← validate DBPM tenant databases exist
+├── phase2-load.mjs ← HTTP load generator (configurable workers, duration)
+├── seed-real-multitenant.mjs ← seed k tenant databases for benchmarking
+├── build-token-pool.mjs ← generate auth tokens for load testing
+├── build-keyspace-profiles.mjs ← build tenant keyspace profiles
+├── build-business-op-profiles.mjs ← build business operation profiles
+├── prepare-public-test-access.mjs ← prepare public API test access
+├── public-test-access-lib.mjs ← shared lib for public test access
+├── reset-business-test-data.mjs ← reset test data between runs
+├── run-comparison.sh ← shell wrapper: run both modes + compare
+└── results/ ← raw JSON benchmark results (gitignored)
+```
+
+---
+
+## Module specifications
+
+### 1. `pg-client-wrapper-plugin.ts`
+
+**Purpose:** Grafast middleware plugin that intercepts `client.query()` to transform SQL per-request.
+
+**Exports:**
+- `PgMultiTenancyWrapperPlugin: GraphileConfig.Plugin`
+
+**Internal functions:**
+- `createSqlTransformProxy(client, transform)` — Proxy wrapping `query()` and `withTransaction()`
+- `wrapWithPgClient(original, contextValue)` — lazy wrapper that reads `pgSqlTextTransform` at call time
+
+**Behavior:**
+1. Runs in `grafast.middleware.prepareArgs` (after `PgContextPlugin`)
+2. Iterates all `pgServices`, wraps each `withPgClient` function on `contextValue`
+3. At execution time, reads `contextValue.pgSqlTextTransform`
+4. If transform exists: proxy `client.query()` to transform `opts.text`
+5. If no transform: pass through unchanged
+6. Also wraps `client.withTransaction()` for transaction-scoped queries
+
+**Dependencies:** None (pure Grafast plugin, no external imports)
+
+### 2. `multi-tenancy-cache.ts`
+
+**Purpose:** Top-level orchestrator — owns the full tenant lifecycle including the `tenantInstances` Map.
+
+**Exports:**
+- `configureMultiTenancyCache({ basePresetBuilder })` — one-time package bootstrap; stores wrapped preset builder internally.
+- `getOrCreateTenantInstance(config)` → `Promise` — resolves tenant, stores in internal `tenantInstances` map. Uses the package-owned wrapped preset builder configured at bootstrap.
+- `getTenantInstance(cacheKey)` → `TenantInstance | undefined` — fast-path lookup from internal map
+- `flushTenantInstance(cacheKey)` — evict from `tenantInstances` map + deregister from template refCount
+- `getMultiTenancyCacheStats()` → `MultiTenancyCacheStats`
+- `shutdownMultiTenancyCache()` — release all resources (templates, dedicated instances, introspection cache, tenantInstances)
+- Types: `TenantConfig`, `TenantInstance`, `MultiTenancyCacheStats`
+
+**Internal state:**
+- `tenantInstances: Map` — fast-path cache of resolved tenant instances
+- `creatingTenants: Map>` — single-flight for tenant creation
+- `creatingTemplates: Map>` — single-flight for template creation
+- `dedicatedInstances: Map` — fallback non-shared instances, tracked with lifecycle metadata (createdAt, lastUsedAt, source=introspection-failure)
+
+**Flow (getOrCreateTenantInstance):**
+1. Check `tenantInstances` map (fast path) → return if hit
+2. Check `creatingTenants` map (single-flight coalesce) → wait if in-flight
+3. `getOrCreateIntrospection(pool, schemas, connectionKey)` → fingerprint
+4. `getTemplate(fingerprint)` → hit? → reuse, `registerTenant()`
+5. Miss → check `creatingTemplates` (single-flight for template)
+6. Miss → `createTemplate()` (builds PostGraphile instance, `setTemplate()`)
+7. Build `TenantInstance` with `buildSchemaRemapTransform()` as `sqlTextTransform`
+8. Store in `tenantInstances` map → return
+
+**Preset wrapping (package-owned):**
+`configureMultiTenancyCache()` wraps the base preset builder once to add:
+1. `plugins: [PgMultiTenancyWrapperPlugin]`
+2. `grafast.context` callback that reads `svc_key` from `requestContext.expressv4.req.svc_key`, looks up the tenant's `sqlTextTransform` from the internal `tenantInstances` map, and injects it as `pgSqlTextTransform` on the Grafast context — **no `req.sqlTextTransform` field needed on Express.Request**
+
+**Fallback:** If introspection fails, creates a dedicated (non-shared) instance (resilience over visibility).
+Fallback instances MUST be lifecycle-bound: cleaned by `flushTenantInstance()`, swept by idle TTL/LRU, and always released by `shutdownMultiTenancyCache()` so they cannot linger after cache flush/eviction.
+
+**Dependencies:** `introspection-cache`, `registry-template-map`, `utils/sql-transform`, `utils/schema-map`, `postgraphile`, `grafserv`, `express`
+
+### 3. `registry-template-map.ts`
+
+**Purpose:** Global template registry with lifecycle management.
+
+**Exports:**
+- `getTemplate(fingerprint)` → `RegistryTemplate | undefined`
+- `setTemplate(fingerprint, template)`
+- `registerTenant(cacheKey, fingerprint)` — increment refCount
+- `deregisterTenant(cacheKey)` — decrement refCount, mark idle
+- `sweepIdleTemplates()` — evict expired + over-cap templates
+- `clearAllTemplates()` — shutdown cleanup
+- `getTemplateStats()` — diagnostic stats
+- `_testSetMaxTemplates(n)` — test-only hook
+- Type: `RegistryTemplate`
+
+**Eviction policy:**
+- **TTL:** Templates with `refCount === 0` and `idleSince` older than 30min are evicted
+- **LRU cap:** When `templateMap.size > MAX_TEMPLATES` (50), oldest idle templates evicted first
+- **Periodic sweep:** Every 5min (lazy-started, `unref()`'d for clean exit)
+- **Active protection:** Templates with `refCount > 0` are never evicted
+- **Cleanup:** `disposeTemplate()` calls `pgl.release()` + `httpServer.close()`
+
+### 4. `introspection-cache.ts`
+
+**Purpose:** In-memory cache for parsed introspection results + fingerprints.
+
+**Exports:**
+- `getOrCreateIntrospection(pool, schemas, connectionKey)` → `Promise`
+- `invalidateIntrospection(connectionKey, schemas?)` — targeted invalidation
+- `clearIntrospectionCache()` — full clear + stop sweep timer
+- `sweepIntrospectionCache()` — evict expired + over-cap entries
+- `getIntrospectionCacheStats()` → `IntrospectionCacheStats`
+- `_testSetMaxEntries(n)` — test-only hook
+- Types: `CachedIntrospection`, `IntrospectionCacheStats`
+
+**Key:** `connHash:schema1,schema2` (schemas sorted alphabetically)
+`connHash` is derived from normalized connection identity:
+- `host`, `port`, `database`, `user` (and connection mode such as `sslmode`/socket when relevant)
+- Canonicalized + hashed to avoid leaking credentials while preventing cross-environment collisions.
+
+**Eviction policy:** Same pattern as template cache — TTL (30min idle) + LRU cap (100 entries) + periodic sweep (5min).
+
+**Single-flight:** `inflight` Map coalesces concurrent requests. `finally` block guarantees cleanup. Failed entries are NOT cached.
+
+### 5. `utils/fingerprint.ts`
+
+**Purpose:** Schema-name-agnostic structural fingerprinting.
+
+**Exports:**
+- `getSchemaFingerprint(introspection, schemaNames?)` → SHA-256 hex string
+- `fingerprintsMatch(a, b)` → boolean
+- Types: `MinimalIntrospection`, `IntrospectionClass`, `IntrospectionAttribute`, `IntrospectionConstraint`, `IntrospectionType`, `IntrospectionNamespace`, `IntrospectionProc`
+
+**What's included in fingerprint:** Table names, column names, data types, constraints, function signatures.
+
+**What's excluded:** Schema/namespace names, OIDs, instance-specific identifiers. This ensures `t_1_services_public.apis` and `t_2_services_public.apis` produce the same fingerprint.
+
+### 6. `utils/sql-transform.ts`
+
+**Purpose:** SQL remap engine with AST-safe rewrite and cache-backed fast path.
+
+**Exports:**
+- `buildSchemaRemapTransform(schemaMap)` → `(text: string) => string`
+
+**How it works:**
+1. Computes a cache key from `(sqlTextHash, schemaMapHash)`
+2. On cache hit: returns pre-rewritten SQL immediately (hot path)
+3. On cache miss: `parse -> rewrite semantic schema nodes -> deparse`
+4. Stores rewritten SQL in LRU/TTL cache for subsequent hits
+5. Empty schema map → identity function (no-op)
+
+### SQL Remap Safety Contract (v3)
+
+The SQL remap layer MUST follow these rules:
+
+1. **Semantic rewrite only**
+- Rewrite schema names via PostgreSQL AST node fields (e.g. relation namespace/schema), not global text substitution.
+- Do not rewrite literals, comments, dollar-quoted blocks, aliases, or unqualified identifiers.
+
+2. **Fail-closed by default**
+- If parse/rewrite/deparse fails, do not silently pass-through the original SQL.
+- Default behavior is request failure with structured error and telemetry.
+- Optional rollout mode may fallback to dedicated handler, but must be explicitly enabled and metered.
+
+3. **Cache-backed performance model**
+- Hot path is cache lookup only.
+- AST parse/rewrite/deparse occurs only on cache miss.
+- Cache policy: bounded LRU + TTL.
+
+4. **Observability requirements**
+- Emit counters/histograms for hit/miss, rewrite latency, rewrite failures, and fallback usage (if enabled).
+- Include tenant key and SQL hash in sampled debug logs.
+
+5. **Validation requirements**
+- Tests must prove that schema-qualified identifiers are remapped correctly.
+- Tests must prove literals/comments/dollar-quoted content are unchanged.
+- Transaction path (`withTransaction`) must apply identical remap behavior.
+
+### 7. `utils/schema-map.ts`
+
+**Purpose:** Schema mapping and pgSettings helpers.
+
+**Exports:**
+- `buildSchemaMap(templateSchemas, tenantSchemas)` → `Record`
+- `buildTenantPgSettings(tenantSchemas)` → `Record` (includes `search_path`)
+- `remapSchemas(templateSchemas, templatePrefix, tenantPrefix)` → `string[]`
+- Type: `SchemaMapping`
+
+### 8. `utils/introspection-query.ts`
+
+**Purpose:** Low-level introspection fetch + parse.
+
+**Exports:**
+- `fetchIntrospection(pool, schemas)` → raw JSON string
+- `parseIntrospection(text)` → `MinimalIntrospection`
+- `fetchAndParseIntrospection(pool, schemas)` → `{ raw, parsed }`
+
+**Connection safety:** Uses `BEGIN` + `SET LOCAL search_path` + `COMMIT` so the search_path never leaks to pooled connections.
+
+---
+
+## Server integration
+
+The server is a thin consumer of the package APIs. It does **not** manage tenant
+state, preset wrapping logic, or Express.Request extensions — those responsibilities
+belong to the package.
+
+### `graphile.ts` changes
+
+**New function: `multiTenancyHandler(opts)`**
+- Selected when `opts.api.useMultiTenancyCache === true`
+- Calls `configureMultiTenancyCache({ basePresetBuilder })` once at startup (package owns wrapping)
+- Calls `getTenantInstance(key)` for fast-path cache hit
+- On miss, calls `getOrCreateTenantInstance(config)` — no preset builder passed from server
+- Routes the request to `tenant.handler(req, res, next)` — the package's Grafast context callback handles `pgSqlTextTransform` injection internally (no `req.sqlTextTransform` needed)
+
+**New exports:**
+- `isMultiTenancyCacheEnabled(opts)` — boolean check
+- `shutdownMultiTenancy()` — calls package's `shutdownMultiTenancyCache()`
+
+**No changes to `types.ts`** — `Express.Request` is NOT extended with `sqlTextTransform`. The transform is injected directly into the Grafast context by the package's preset builder using the existing `req.svc_key`.
+
+### `flush.ts` changes
+
+**New function: `createFlushMiddleware(opts)`**
+- Replaces `flush` (deprecated but kept for backwards compat)
+- Calls package's `flushTenantInstance(key)` + `invalidateIntrospection(connectionKey)`
+
+**`flushService()` changes:**
+- When multi-tenancy enabled: resolves canonical `connectionKey` from `databaseId`, calls `invalidateIntrospection(connectionKey)` + `flushTenantInstance(key)` for each matching domain
+- Flush path must also release any tenant-bound fallback dedicated instances immediately.
+
+### `env.ts` + `graphile.ts` (types) changes
+
+- Add `USE_MULTI_TENANCY_CACHE` env var → `api.useMultiTenancyCache: boolean`
+- Default: `false` (opt-in)
+
+---
+
+## Activation
+
+```bash
+# Enable multi-tenancy cache
+USE_MULTI_TENANCY_CACHE=true
+
+# For old (dedicated) mode, enlarge cache to avoid eviction churn:
+# GRAPHILE_CACHE_MAX= where K = tenant count (min 100)
+```
+
+When `useMultiTenancyCache` is `false` (default), the server uses the existing `graphile-cache` (one PostGraphile instance per `svc_key`) — zero behavioral change.
+
+---
+
+## Dependencies
+
+```json
+{
+ "dependencies": {
+ "@pgpmjs/logger": "workspace:^",
+ "express": "^5.2.1",
+ "grafserv": "1.0.0",
+ "graphile-config": "1.0.0",
+ "pg": "^8.11.3",
+ "pg-env": "workspace:^",
+ "pg-introspection": "1.0.0",
+ "pgsql-deparser": "^17.18.2",
+ "pgsql-parser": "^17.9.14",
+ "postgraphile": "5.0.0"
+ },
+ "devDependencies": {
+ "@types/express": "^5.0.6",
+ "@types/pg": "^8.10.9",
+ "makage": "^0.3.0",
+ "ts-node": "^10.9.2"
+ }
+}
+```
+
+No Crystal fork. No `link:` overrides. Works with published Crystal/PostGraphile packages.
+
+---
+
+## Test plan
+
+### Unit tests (in `src/__tests__/`)
+
+| Test file | Coverage |
+|---|---|
+| `pg-client-wrapper-plugin.test.ts` | Proxy intercepts query/withTransaction, lazy transform read, no-op passthrough, release preservation |
+| `registry-template-map.test.ts` | register/deregister refCount, TTL eviction, LRU cap eviction, active-template protection, sweep timer, exact-cap boundary |
+| `introspection-cache.test.ts` | Cache hit/miss, single-flight coalescing, failure retry, TTL eviction, LRU cap eviction, invalidation |
+| `fingerprint.test.ts` | Same-structure-different-schema → same fingerprint, different-structure → different fingerprint, constraint normalization |
+| `sql-transform.test.ts` | AST schema-node rewrite, cache hit/miss path, identity transform, multi-schema remap, literal/comment non-rewrite |
+| `schema-map.test.ts` | Schema mapping, pgSettings generation, prefix remapping |
+| `single-flight.test.ts` | Concurrent creation coalescing, failure propagation |
+
+### E2E validation
+
+- Start server with `USE_MULTI_TENANCY_CACHE=true`
+- Send requests for k tenants with identical schemas
+- Assert: 0 errors, template count = 1, all tenants sharing
+- Compare QPS/latency/memory vs dedicated mode
+- Run a **fresh 3-minute benchmark** on the v3 no-Crystal path; do not reuse v2 data
+- Store raw benchmark JSON + environment metadata under `graphql/server/perf/results/`
+
+---
+
+## Historical v2 baseline (reference only, k=20)
+
+| Metric | Dedicated (Old) | Multi-tenant (New) | Improvement |
+|---|---|---|---|
+| QPS | 706 | 780 | +10.5% |
+| p50 latency | 11ms | 11ms | same |
+| p99 latency | 42ms | 29ms | -31% |
+| Heap growth | +1,276 MB | +334 MB | 73.8% less |
+| RSS growth | +1,697 MB | +845 MB | 50.2% less |
+| PostGraphile builds | 20 | 0 | eliminated |
+| Cold start (2nd+) | 412ms | 7ms | 98.3% faster |
+
+This table is historical context from v2 only; it is **not** acceptance evidence for v3.
+
+## v3 performance acceptance (required, no-Crystal path)
+
+Acceptance MUST be based on fresh v3 benchmark runs using published Crystal/PostGraphile packages (no Crystal fork, no `link:` overrides):
+
+1. Benchmark duration: minimum **3 minutes** continuous load per mode (dedicated vs v3 multi-tenancy cache).
+2. Correctness gate: 0 request errors and expected tenant routing behavior.
+3. Performance gate: v3 multi-tenancy cache must show measurable improvement vs dedicated mode in at least one primary metric (QPS or p99 latency), without material regressions in stability.
+4. Resource gate: memory growth (heap/RSS) in v3 multi-tenancy cache must be no worse than dedicated mode under the same run conditions.
+5. Provenance gate: attach raw result files and run metadata (commit SHA, env flags, tenant count, concurrency, duration) in `graphql/server/perf/results/`.
+
+*Old mode given `GRAPHILE_CACHE_MAX=120` (best-case, zero eviction).*
+
+---
+
+## Implementation order
+
+1. **Package scaffolding** — `package.json`, tsconfig, jest config
+2. **Utilities** — `utils/fingerprint.ts`, `utils/sql-transform.ts`, `utils/schema-map.ts`, `utils/introspection-query.ts`
+3. **Cache layers** — `introspection-cache.ts`, `registry-template-map.ts`
+4. **Plugin** — `pg-client-wrapper-plugin.ts`
+5. **Orchestrator** — `multi-tenancy-cache.ts`
+6. **Public API** — `index.ts`
+7. **Server integration** — `middleware/graphile.ts`, `flush.ts`, `env.ts`, `types/graphile.ts`, `server.ts`, `index.ts`
+8. **Tests** — unit tests for all modules
+9. **Benchmark scripts** — `graphql/server/perf/` (e2e load testing framework)
+10. **Validation** — e2e test run
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..68888ff254
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "graphile-multi-tenancy-cache",
+ "version": "0.1.0",
+ "author": "Constructive ",
+ "description": "Template-based multi-tenancy cache for PostGraphile v5 — shares instances across tenants with identical schemas",
+ "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",
+ "template",
+ "constructive",
+ "v5"
+ ],
+ "bugs": {
+ "url": "https://github.com/constructive-io/constructive/issues"
+ },
+ "dependencies": {
+ "@pgpmjs/logger": "workspace:^",
+ "express": "^5.2.1",
+ "grafserv": "1.0.0",
+ "graphile-config": "1.0.0",
+ "pg": "^8.11.3",
+ "pg-env": "workspace:^",
+ "pg-introspection": "1.0.0",
+ "pgsql-deparser": "^17.18.2",
+ "pgsql-parser": "^17.9.14",
+ "postgraphile": "5.0.0"
+ },
+ "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..f6d6195a6d
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
@@ -0,0 +1,1098 @@
+/**
+ * Unit tests for buildKey-based handler caching (v4-buildkey).
+ *
+ * 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
+ */
+
+import { createHash } from 'node:crypto';
+
+// 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 16-char hex string', () => {
+ const pool = makeMockPool();
+ const key = computeBuildKey(pool, ['public'], 'anon', 'authenticated');
+ expect(key).toMatch(/^[0-9a-f]{16}$/);
+ });
+
+ 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);
+ });
+});
+
+// --- Orchestrator tests (require mocking PostGraphile) ---
+
+// Mock the heavy dependencies before importing the orchestrator
+jest.mock('postgraphile', () => ({
+ postgraphile: jest.fn(() => ({
+ createServ: jest.fn(() => ({
+ addTo: jest.fn(async () => {}),
+ ready: jest.fn(async () => {}),
+ })),
+ release: jest.fn(async () => {}),
+ })),
+}));
+
+jest.mock('grafserv/express/v4', () => ({
+ grafserv: 'mock-grafserv',
+}));
+
+jest.mock('express', () => {
+ const mockExpress = jest.fn(() => {
+ const app = jest.fn();
+ return app;
+ });
+ return mockExpress;
+});
+
+jest.mock('node:http', () => ({
+ createServer: jest.fn(() => ({
+ listening: false,
+ close: jest.fn((cb: () => void) => cb()),
+ })),
+}));
+
+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,
+} from '../multi-tenancy-cache';
+
+const mockPresetBuilder = jest.fn((_pool: import('pg').Pool, _schemas: string[], _anon: string, _role: string): import('graphile-config').GraphileConfig.Preset => ({
+ extends: [] as import('graphile-config').GraphileConfig.Preset[],
+ pgServices: [] as never[],
+}));
+
+beforeEach(async () => {
+ await shutdownMultiTenancyCache();
+ configureMultiTenancyCache({ basePresetBuilder: mockPresetBuilder });
+ mockPresetBuilder.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);
+
+ // Preset builder called only once (deduplication)
+ expect(mockPresetBuilder).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(mockPresetBuilder).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);
+ });
+});
+
+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);
+ });
+});
+
+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 preset builder throw to simulate handler creation failure
+ const failingBuilder = jest.fn(() => {
+ throw new Error('simulated build failure');
+ });
+ configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+
+ 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 failingBuilder = jest.fn(() => {
+ throw new Error('simulated build failure');
+ });
+ configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+
+ 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).toMatch(/^[0-9a-f]{16}$/);
+ });
+});
+
+// --- 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 failingBuilder = jest.fn(() => { throw new Error('coalesced fail'); });
+ configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+
+ 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 failingBuilder = jest.fn(() => { throw new Error('coalesced fail 3+'); });
+ configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+
+ 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) preset builder 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)', () => {
+ /**
+ * Access the module-level postgraphile mock so we can override it
+ * per-test with gate-controlled behaviour.
+ */
+ const pgMock = () =>
+ (jest.requireMock('postgraphile') as { postgraphile: jest.Mock }).postgraphile;
+
+ /**
+ * Install a gated postgraphile mock. Each call to `postgraphile()`
+ * creates a new gate; `serv.ready()` blocks until the gate is resolved.
+ * Returns the ordered array of gates so the test can resolve them in
+ * any desired order.
+ */
+ function installGatedMock(): Array<{ resolve: () => void }> {
+ const gates: Array<{ resolve: () => void }> = [];
+ pgMock().mockImplementation(() => {
+ let resolve!: () => void;
+ const promise = new Promise((r) => {
+ resolve = r;
+ });
+ gates.push({ resolve });
+ return {
+ createServ: jest.fn(() => ({
+ addTo: jest.fn(async () => {}),
+ ready: jest.fn(async () => {
+ await promise;
+ }),
+ })),
+ release: jest.fn(async () => {}),
+ };
+ });
+ return gates;
+ }
+
+ afterEach(() => {
+ // Restore the default (instant-resolving) mock so other tests are unaffected
+ pgMock().mockImplementation(() => ({
+ createServ: jest.fn(() => ({
+ addTo: jest.fn(async () => {}),
+ ready: jest.fn(async () => {}),
+ })),
+ release: jest.fn(async () => {}),
+ }));
+ });
+
+ it('newer request finishes first — final mapping stays on newer buildKey', async () => {
+ const gates = installGatedMock();
+ 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 = installGatedMock();
+ 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 = installGatedMock();
+ 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/__tests__/fingerprint.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/fingerprint.test.ts
new file mode 100644
index 0000000000..b743697d6c
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/fingerprint.test.ts
@@ -0,0 +1,125 @@
+import { getSchemaFingerprint, type MinimalIntrospection } from '../utils/fingerprint';
+
+/**
+ * Helper: build a minimal introspection fixture for two tenants
+ * with identical structure but different schema names.
+ */
+function makeIntrospection(schemaName: string, schemaOid: string): MinimalIntrospection {
+ return {
+ namespaces: [
+ { nspname: schemaName, oid: schemaOid },
+ { nspname: 'pg_catalog', oid: '11' },
+ ],
+ classes: [
+ { relname: 'users', relnamespace: schemaOid, relkind: 'r' },
+ { relname: 'posts', relnamespace: schemaOid, relkind: 'r' },
+ ],
+ attributes: [
+ { attrelid: 'users', attname: 'id', atttypid: '23', attnum: 1, attnotnull: true },
+ { attrelid: 'users', attname: 'name', atttypid: '25', attnum: 2, attnotnull: false },
+ { attrelid: 'posts', attname: 'id', atttypid: '23', attnum: 1, attnotnull: true },
+ { attrelid: 'posts', attname: 'title', atttypid: '25', attnum: 2, attnotnull: false },
+ { attrelid: 'posts', attname: 'user_id', atttypid: '23', attnum: 3, attnotnull: true },
+ ],
+ constraints: [
+ { conname: 'users_pkey', conrelid: 'users', contype: 'p', conkey: [1], confrelid: null, confkey: null },
+ { conname: 'posts_pkey', conrelid: 'posts', contype: 'p', conkey: [1], confrelid: null, confkey: null },
+ { conname: 'posts_user_id_fkey', conrelid: 'posts', contype: 'f', conkey: [3], confrelid: 'users', confkey: [1] },
+ ],
+ types: [
+ { typname: 'int4', typnamespace: schemaOid, typtype: 'b' },
+ ],
+ procs: [
+ { proname: 'get_user', pronamespace: schemaOid, proargtypes: '23', prorettype: '2249', provolatile: 's' },
+ ],
+ };
+}
+
+describe('getSchemaFingerprint', () => {
+ it('produces the same fingerprint for identical structures with different schema names', () => {
+ const tenant1 = makeIntrospection('t_1_services_public', '100');
+ const tenant2 = makeIntrospection('t_2_services_public', '200');
+
+ const fp1 = getSchemaFingerprint(tenant1, ['t_1_services_public']);
+ const fp2 = getSchemaFingerprint(tenant2, ['t_2_services_public']);
+
+ expect(fp1).toBe(fp2);
+ });
+
+ it('produces different fingerprints when table structure differs', () => {
+ const tenant1 = makeIntrospection('t_1_services_public', '100');
+ const tenant2 = makeIntrospection('t_2_services_public', '200');
+
+ // Add an extra column to tenant2
+ tenant2.attributes.push({
+ attrelid: 'posts',
+ attname: 'body',
+ atttypid: '25',
+ attnum: 4,
+ attnotnull: false,
+ });
+
+ const fp1 = getSchemaFingerprint(tenant1, ['t_1_services_public']);
+ const fp2 = getSchemaFingerprint(tenant2, ['t_2_services_public']);
+
+ expect(fp1).not.toBe(fp2);
+ });
+
+ it('produces different fingerprints when constraint structure differs', () => {
+ const tenant1 = makeIntrospection('t_1_services_public', '100');
+ const tenant2 = makeIntrospection('t_2_services_public', '200');
+
+ // Add a unique constraint to tenant2
+ tenant2.constraints.push({
+ conname: 'users_name_unique',
+ conrelid: 'users',
+ contype: 'u',
+ conkey: [2],
+ confrelid: null,
+ confkey: null,
+ });
+
+ const fp1 = getSchemaFingerprint(tenant1, ['t_1_services_public']);
+ const fp2 = getSchemaFingerprint(tenant2, ['t_2_services_public']);
+
+ expect(fp1).not.toBe(fp2);
+ });
+
+ it('returns a valid SHA-256 hex string', () => {
+ const introspection = makeIntrospection('public', '100');
+ const fp = getSchemaFingerprint(introspection);
+
+ expect(fp).toMatch(/^[a-f0-9]{64}$/);
+ });
+
+ it('is deterministic for the same input', () => {
+ const introspection = makeIntrospection('public', '100');
+
+ const fp1 = getSchemaFingerprint(introspection);
+ const fp2 = getSchemaFingerprint(introspection);
+
+ expect(fp1).toBe(fp2);
+ });
+
+ it('filters by schemaNames when provided', () => {
+ const introspection: MinimalIntrospection = {
+ namespaces: [
+ { nspname: 'schema_a', oid: '100' },
+ { nspname: 'schema_b', oid: '200' },
+ ],
+ classes: [
+ { relname: 'users', relnamespace: '100', relkind: 'r' },
+ { relname: 'orders', relnamespace: '200', relkind: 'r' },
+ ],
+ attributes: [],
+ constraints: [],
+ types: [],
+ procs: [],
+ };
+
+ const fpA = getSchemaFingerprint(introspection, ['schema_a']);
+ const fpBoth = getSchemaFingerprint(introspection, ['schema_a', 'schema_b']);
+
+ expect(fpA).not.toBe(fpBoth);
+ });
+});
diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts
new file mode 100644
index 0000000000..7cb0e5d33b
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts
@@ -0,0 +1,87 @@
+import {
+ getConnectionKey,
+ invalidateIntrospection,
+ clearIntrospectionCache,
+ getIntrospectionCacheStats,
+} from '../introspection-cache';
+
+afterEach(() => {
+ clearIntrospectionCache();
+});
+
+describe('introspection-cache', () => {
+ describe('getConnectionKey', () => {
+ it('returns a hex string from pool options', () => {
+ const mockPool = {
+ options: {
+ host: 'localhost',
+ port: 5432,
+ database: 'testdb',
+ user: 'testuser',
+ ssl: false,
+ },
+ } as any;
+
+ const key = getConnectionKey(mockPool);
+ expect(key).toMatch(/^[a-f0-9]{16}$/);
+ });
+
+ it('returns same key for same pool options', () => {
+ const pool1 = { options: { host: 'localhost', port: 5432, database: 'db1', user: 'u', ssl: false } } as any;
+ const pool2 = { options: { host: 'localhost', port: 5432, database: 'db1', user: 'u', ssl: false } } as any;
+
+ expect(getConnectionKey(pool1)).toBe(getConnectionKey(pool2));
+ });
+
+ it('returns different keys for different databases', () => {
+ const pool1 = { options: { host: 'localhost', port: 5432, database: 'db1', user: 'u', ssl: false } } as any;
+ const pool2 = { options: { host: 'localhost', port: 5432, database: 'db2', user: 'u', ssl: false } } as any;
+
+ expect(getConnectionKey(pool1)).not.toBe(getConnectionKey(pool2));
+ });
+
+ it('returns different keys for different hosts', () => {
+ const pool1 = { options: { host: 'host1', port: 5432, database: 'db', user: 'u', ssl: false } } as any;
+ const pool2 = { options: { host: 'host2', port: 5432, database: 'db', user: 'u', ssl: false } } as any;
+
+ expect(getConnectionKey(pool1)).not.toBe(getConnectionKey(pool2));
+ });
+
+ it('distinguishes ssl vs nossl', () => {
+ const pool1 = { options: { host: 'h', port: 5432, database: 'db', user: 'u', ssl: false } } as any;
+ const pool2 = { options: { host: 'h', port: 5432, database: 'db', user: 'u', ssl: true } } as any;
+
+ expect(getConnectionKey(pool1)).not.toBe(getConnectionKey(pool2));
+ });
+ });
+
+ describe('invalidateIntrospection', () => {
+ it('does not throw when invalidating non-existent key', () => {
+ expect(() => invalidateIntrospection('nonexistent')).not.toThrow();
+ });
+
+ it('does not throw when invalidating with schemas', () => {
+ expect(() => invalidateIntrospection('nonexistent', ['public'])).not.toThrow();
+ });
+ });
+
+ describe('clearIntrospectionCache', () => {
+ it('resets stats to zero', () => {
+ clearIntrospectionCache();
+ const stats = getIntrospectionCacheStats();
+ expect(stats.size).toBe(0);
+ expect(stats.inflightCount).toBe(0);
+ });
+ });
+
+ describe('getIntrospectionCacheStats', () => {
+ it('returns stats object with expected shape', () => {
+ const stats = getIntrospectionCacheStats();
+ expect(stats).toHaveProperty('size');
+ expect(stats).toHaveProperty('maxSize');
+ expect(stats).toHaveProperty('inflightCount');
+ expect(typeof stats.size).toBe('number');
+ expect(typeof stats.maxSize).toBe('number');
+ });
+ });
+});
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..2fe40d1df7
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/index.ts
@@ -0,0 +1,38 @@
+// --- Orchestrator (primary API) ---
+export {
+ configureMultiTenancyCache,
+ getOrCreateTenantInstance,
+ getTenantInstance,
+ flushTenantInstance,
+ flushByDatabaseId,
+ getMultiTenancyCacheStats,
+ shutdownMultiTenancyCache,
+ computeBuildKey,
+ getBuildKeyForSvcKey,
+} from './multi-tenancy-cache';
+
+export type {
+ TenantConfig,
+ TenantInstance,
+ MultiTenancyCacheStats,
+ MultiTenancyCacheConfig,
+} from './multi-tenancy-cache';
+
+// --- Introspection cache (kept as module/test base — not required for v4 runtime) ---
+export {
+ getOrCreateIntrospection,
+ invalidateIntrospection,
+ clearIntrospectionCache,
+ getIntrospectionCacheStats,
+ getConnectionKey,
+} from './introspection-cache';
+
+export type {
+ CachedIntrospection,
+ IntrospectionCacheStats,
+} from './introspection-cache';
+
+// --- Utilities (kept as module base — not required for v4 runtime) ---
+export { getSchemaFingerprint } from './utils/fingerprint';
+export type { MinimalIntrospection } from './utils/fingerprint';
+export { fetchIntrospection, parseIntrospection, fetchAndParseIntrospection } from './utils/introspection-query';
diff --git a/graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts b/graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts
new file mode 100644
index 0000000000..4688beec5c
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts
@@ -0,0 +1,232 @@
+import crypto from 'node:crypto';
+import { Logger } from '@pgpmjs/logger';
+import type { Pool } from 'pg';
+import { fetchAndParseIntrospection } from './utils/introspection-query';
+import { getSchemaFingerprint, type MinimalIntrospection } from './utils/fingerprint';
+
+const log = new Logger('introspection-cache');
+
+// --- Configuration ---
+const MAX_ENTRIES = 100;
+const TTL_MS = 30 * 60 * 1000; // 30 minutes idle
+const SWEEP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
+
+// Test-only hook
+let maxEntries = MAX_ENTRIES;
+
+// --- Types ---
+
+export interface CachedIntrospection {
+ parsed: MinimalIntrospection;
+ fingerprint: string;
+ raw: string;
+ createdAt: number;
+ lastUsedAt: number;
+}
+
+export interface IntrospectionCacheStats {
+ size: number;
+ maxSize: number;
+ inflightCount: number;
+}
+
+// --- Internal state ---
+
+const cache = new Map();
+const inflight = new Map>();
+let sweepTimer: ReturnType | null = null;
+
+/**
+ * Compute a connection hash from pool options.
+ * Canonicalized + hashed to avoid leaking credentials while
+ * preventing cross-environment collisions.
+ */
+function computeConnectionHash(pool: Pool): string {
+ const opts = (pool as any).options || {};
+ const parts = [
+ opts.host || 'localhost',
+ String(opts.port || 5432),
+ opts.database || '',
+ opts.user || '',
+ opts.ssl ? 'ssl' : 'nossl',
+ ];
+ return crypto.createHash('sha256').update(parts.join(':')).digest('hex').slice(0, 16);
+}
+
+/**
+ * Build the cache key: connHash:schema1,schema2 (sorted alphabetically).
+ */
+function buildCacheKey(connectionKey: string, schemas: string[]): string {
+ const sorted = [...schemas].sort();
+ return `${connectionKey}:${sorted.join(',')}`;
+}
+
+/**
+ * Derive a connection key from a pool.
+ */
+export function getConnectionKey(pool: Pool): string {
+ return computeConnectionHash(pool);
+}
+
+function ensureSweepTimer(): void {
+ if (sweepTimer) return;
+ sweepTimer = setInterval(() => {
+ sweepIntrospectionCache();
+ }, SWEEP_INTERVAL_MS);
+ if (sweepTimer.unref) sweepTimer.unref();
+}
+
+// --- Public API ---
+
+/**
+ * Get or create a cached introspection result.
+ *
+ * Single-flight: concurrent requests for the same key coalesce.
+ * Failed entries are NOT cached.
+ *
+ * @param pool - PostgreSQL connection pool
+ * @param schemas - Schema names to introspect
+ * @param connectionKey - Pre-computed connection key (use getConnectionKey())
+ * @returns Cached introspection with fingerprint
+ */
+export async function getOrCreateIntrospection(
+ pool: Pool,
+ schemas: string[],
+ connectionKey: string,
+): Promise {
+ const cacheKey = buildCacheKey(connectionKey, schemas);
+
+ // Cache hit
+ const existing = cache.get(cacheKey);
+ if (existing) {
+ existing.lastUsedAt = Date.now();
+ return existing;
+ }
+
+ // Single-flight coalesce
+ const pending = inflight.get(cacheKey);
+ if (pending) {
+ return pending;
+ }
+
+ // Cache miss — create
+ const promise = doIntrospect(pool, schemas, cacheKey);
+ inflight.set(cacheKey, promise);
+
+ try {
+ const result = await promise;
+ return result;
+ } finally {
+ inflight.delete(cacheKey);
+ }
+}
+
+async function doIntrospect(
+ pool: Pool,
+ schemas: string[],
+ cacheKey: string,
+): Promise {
+ const { raw, parsed } = await fetchAndParseIntrospection(pool, schemas);
+ const fingerprint = getSchemaFingerprint(parsed, schemas);
+
+ const entry: CachedIntrospection = {
+ parsed,
+ fingerprint,
+ raw,
+ createdAt: Date.now(),
+ lastUsedAt: Date.now(),
+ };
+
+ cache.set(cacheKey, entry);
+ ensureSweepTimer();
+
+ log.debug(`Cached introspection key=${cacheKey} fingerprint=${fingerprint.slice(0, 12)}…`);
+ return entry;
+}
+
+/**
+ * Targeted invalidation by connection key and optional schemas.
+ */
+export function invalidateIntrospection(
+ connectionKey: string,
+ schemas?: string[],
+): void {
+ if (schemas) {
+ const cacheKey = buildCacheKey(connectionKey, schemas);
+ cache.delete(cacheKey);
+ log.debug(`Invalidated introspection key=${cacheKey}`);
+ } else {
+ // Invalidate all entries matching this connection key
+ const prefix = `${connectionKey}:`;
+ for (const key of cache.keys()) {
+ if (key.startsWith(prefix)) {
+ cache.delete(key);
+ }
+ }
+ log.debug(`Invalidated all introspection entries for connKey=${connectionKey}`);
+ }
+}
+
+/**
+ * Full cache clear + stop sweep timer.
+ */
+export function clearIntrospectionCache(): void {
+ cache.clear();
+ inflight.clear();
+ if (sweepTimer) {
+ clearInterval(sweepTimer);
+ sweepTimer = null;
+ }
+ log.debug('Introspection cache cleared');
+}
+
+/**
+ * Evict expired + over-cap entries.
+ */
+export function sweepIntrospectionCache(): void {
+ const now = Date.now();
+ const expired: string[] = [];
+
+ for (const [key, entry] of cache) {
+ if (now - entry.lastUsedAt > TTL_MS) {
+ expired.push(key);
+ }
+ }
+
+ for (const key of expired) {
+ cache.delete(key);
+ }
+
+ // LRU cap
+ if (cache.size > maxEntries) {
+ const sorted = [...cache.entries()].sort(
+ (a, b) => a[1].lastUsedAt - b[1].lastUsedAt,
+ );
+ const toEvict = sorted.slice(0, cache.size - maxEntries);
+ for (const [key] of toEvict) {
+ cache.delete(key);
+ }
+ }
+
+ if (expired.length > 0 || cache.size > maxEntries) {
+ log.debug(`Introspection cache sweep: evicted=${expired.length} size=${cache.size}`);
+ }
+}
+
+/**
+ * Get diagnostic stats.
+ */
+export function getIntrospectionCacheStats(): IntrospectionCacheStats {
+ return {
+ size: cache.size,
+ maxSize: maxEntries,
+ inflightCount: inflight.size,
+ };
+}
+
+/**
+ * Test-only hook to set max entries.
+ */
+export function _testSetMaxEntries(n: number): void {
+ maxEntries = n;
+}
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..157aef2d72
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
@@ -0,0 +1,476 @@
+/**
+ * Multi-tenancy cache orchestrator (v4-buildkey).
+ *
+ * Caches one independent PostGraphile handler per **buildKey** (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 { createHash } from 'node:crypto';
+import { createServer } from 'node:http';
+import { Logger } from '@pgpmjs/logger';
+import express from 'express';
+import { postgraphile } from 'postgraphile';
+import { grafserv } from 'grafserv/express/v4';
+import type { Pool } from 'pg';
+import type { GraphileConfig } from 'graphile-config';
+
+const log = new Logger('multi-tenancy-cache');
+
+// --- Types ---
+
+export interface TenantConfig {
+ svcKey: string;
+ pool: Pool;
+ schemas: string[];
+ anonRole: string;
+ roleName: string;
+ databaseId?: string;
+}
+
+export interface TenantInstance {
+ buildKey: string;
+ handler: import('express').Express;
+ schemas: string[];
+ pgl: import('postgraphile').PostGraphileInstance;
+ httpServer: import('http').Server;
+ createdAt: number;
+ lastUsedAt: number;
+}
+
+export interface MultiTenancyCacheStats {
+ handlerCacheSize: number;
+ svcKeyMappings: number;
+ databaseIdMappings: number;
+ inflightCreations: number;
+}
+
+// --- Internal state ---
+
+/** buildKey → TenantInstance (the real handler cache) */
+const handlerCache = new Map();
+
+/** svc_key → buildKey (routing index) */
+const svcKeyToBuildKey = new Map();
+
+/** databaseId → Set (flush-by-database index) */
+const databaseIdToBuildKeys = new Map>();
+
+/** buildKey → Promise (single-flight coalescing) */
+const creatingHandlers = new Map>();
+
+/**
+ * Per-svc_key monotonic epoch.
+ *
+ * Each call to getOrCreateTenantInstance increments the epoch for its
+ * svc_key. After handler creation completes, the caller only registers
+ * a mapping if its captured epoch still matches the current value.
+ *
+ * This prevents a stale (older, slower) build from overwriting a
+ * newer binding that completed earlier.
+ */
+const svcKeyEpoch = new Map();
+
+/** The preset builder, set once by configureMultiTenancyCache(). */
+let presetBuilder: ((
+ pool: Pool,
+ schemas: string[],
+ anonRole: string,
+ roleName: string,
+) => GraphileConfig.Preset) | null = null;
+
+// --- Configuration ---
+
+export interface MultiTenancyCacheConfig {
+ basePresetBuilder: (
+ pool: Pool,
+ schemas: string[],
+ anonRole: string,
+ roleName: string,
+ ) => GraphileConfig.Preset;
+}
+
+/**
+ * One-time package bootstrap. Stores the preset builder.
+ * Must be called before any getOrCreateTenantInstance() calls.
+ */
+export function configureMultiTenancyCache(config: MultiTenancyCacheConfig): void {
+ presetBuilder = config.basePresetBuilder;
+ log.info('Multi-tenancy cache configured (v4-buildkey — buildKey-based handler caching)');
+}
+
+// --- BuildKey computation ---
+
+/**
+ * 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)
+ */
+function getPoolIdentity(pool: Pool): string {
+ const opts = (pool as unknown as { options: Record }).options || {};
+
+ // Primary path: parse connectionString (matches real pg-cache pool shape)
+ 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 {
+ // If URL parsing fails, use the raw connectionString (will be hashed anyway)
+ return opts.connectionString;
+ }
+ }
+
+ // Fallback: individual fields (for pools created with explicit host/database/user)
+ if (opts.host || opts.database || opts.user) {
+ return `${opts.host || 'localhost'}:${opts.port || 5432}/${opts.database || ''}@${opts.user || ''}`;
+ }
+
+ // Last resort: no identity available — log a warning
+ log.warn('Pool has no connectionString or individual connection fields — buildKey may not be unique');
+ return 'unknown-pool';
+}
+
+/**
+ * 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
+ *
+ * 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,
+): string {
+ const input = JSON.stringify({
+ conn: getPoolIdentity(pool),
+ schemas,
+ anonRole,
+ roleName,
+ });
+ return createHash('sha256').update(input).digest('hex').slice(0, 16);
+}
+
+// --- Index management ---
+
+/**
+ * Register a svc_key → buildKey mapping and update the databaseId index.
+ *
+ * If the svc_key was previously mapped to a different buildKey, the old
+ * mapping is cleaned up first. If no other svc_keys still reference the
+ * old buildKey, it is evicted (handler disposed, indexes purged).
+ */
+function registerMapping(svcKey: string, buildKey: string, databaseId?: string): void {
+ const oldBuildKey = svcKeyToBuildKey.get(svcKey);
+
+ if (oldBuildKey && oldBuildKey !== buildKey) {
+ // Rebinding — detach this svc_key from the old buildKey first
+ svcKeyToBuildKey.delete(svcKey);
+
+ // If old buildKey has no remaining svc_key references, evict it
+ if (getSvcKeysForBuildKey(oldBuildKey).length === 0) {
+ evictBuildKey(oldBuildKey);
+ }
+ }
+
+ svcKeyToBuildKey.set(svcKey, buildKey);
+ if (databaseId) {
+ let keys = databaseIdToBuildKeys.get(databaseId);
+ if (!keys) {
+ keys = new Set();
+ databaseIdToBuildKeys.set(databaseId, keys);
+ }
+ keys.add(buildKey);
+ }
+}
+
+/**
+ * Collect all svc_keys that map to a given buildKey.
+ */
+function getSvcKeysForBuildKey(buildKey: string): string[] {
+ const result: string[] = [];
+ for (const [svcKey, bk] of svcKeyToBuildKey) {
+ if (bk === buildKey) result.push(svcKey);
+ }
+ return result;
+}
+
+/**
+ * Remove a buildKey from all indexes and dispose the handler (if present).
+ *
+ * Also cleans orphaned indexes — if handler creation failed, the handler
+ * won't be in handlerCache but the svc_key and databaseId indexes may
+ * still reference the buildKey. This function cleans those too.
+ */
+function evictBuildKey(buildKey: string): void {
+ const handler = handlerCache.get(buildKey);
+
+ // Always clean indexes, even if handler isn't cached (handles orphaned indexes)
+ handlerCache.delete(buildKey);
+
+ // Remove all svc_key → buildKey mappings pointing to this buildKey
+ for (const svcKey of getSvcKeysForBuildKey(buildKey)) {
+ svcKeyToBuildKey.delete(svcKey);
+ }
+
+ // Remove from databaseId index
+ for (const [dbId, keys] of databaseIdToBuildKeys) {
+ keys.delete(buildKey);
+ if (keys.size === 0) databaseIdToBuildKeys.delete(dbId);
+ }
+
+ if (handler) {
+ disposeTenant(handler).catch((err) => {
+ log.error(`Failed to dispose handler buildKey=${buildKey}:`, err);
+ });
+ }
+
+ log.debug(`Evicted buildKey=${buildKey} (handler=${handler ? 'disposed' : 'none/orphaned'})`);
+}
+
+// --- Core API ---
+
+/**
+ * Fast-path lookup: svc_key → buildKey → handler.
+ */
+export function getTenantInstance(svcKey: string): TenantInstance | undefined {
+ const buildKey = svcKeyToBuildKey.get(svcKey);
+ if (!buildKey) return undefined;
+
+ const handler = handlerCache.get(buildKey);
+ if (handler) {
+ handler.lastUsedAt = Date.now();
+ }
+ return handler;
+}
+
+/**
+ * Resolve the buildKey for a given svc_key (for diagnostics / external use).
+ */
+export function getBuildKeyForSvcKey(svcKey: string): string | undefined {
+ return svcKeyToBuildKey.get(svcKey);
+}
+
+/**
+ * Resolve or create a tenant handler.
+ *
+ * Flow:
+ * 1. Compute buildKey from config's build inputs
+ * 2. Check handlerCache (fast path) → register mapping, return
+ * 3. Check creatingHandlers (single-flight coalesce) → await, register on success
+ * 4. Create a new independent PostGraphile instance keyed by buildKey
+ * 5. On success: register mapping, store in handlerCache, return
+ *
+ * Registration is deferred until AFTER handler creation succeeds. This
+ * ensures that if the shared in-flight promise rejects, NO participating
+ * svc_key leaves orphaned mappings — creator or follower alike.
+ */
+export async function getOrCreateTenantInstance(
+ config: TenantConfig,
+): Promise {
+ const { svcKey, pool, schemas, anonRole, roleName, databaseId } = config;
+
+ if (!presetBuilder) {
+ throw new Error('Multi-tenancy cache not configured. Call configureMultiTenancyCache() first.');
+ }
+
+ const buildKey = computeBuildKey(pool, schemas, anonRole, roleName);
+
+ // Capture a monotonically increasing epoch for this svc_key.
+ // Only the request holding the latest epoch is allowed to register.
+ const epoch = (svcKeyEpoch.get(svcKey) ?? 0) + 1;
+ svcKeyEpoch.set(svcKey, epoch);
+
+ // Step 1: Fast path — handler already cached
+ const existing = handlerCache.get(buildKey);
+ if (existing) {
+ existing.lastUsedAt = Date.now();
+ if (svcKeyEpoch.get(svcKey) === epoch) {
+ registerMapping(svcKey, buildKey, databaseId);
+ }
+ return existing;
+ }
+
+ // Step 2: Single-flight coalesce — handler being created by another request
+ const pending = creatingHandlers.get(buildKey);
+ if (pending) {
+ // Await the shared promise; register only on success
+ const result = await pending;
+ if (svcKeyEpoch.get(svcKey) === epoch) {
+ registerMapping(svcKey, buildKey, databaseId);
+ }
+ return result;
+ }
+
+ // Step 3: Creator path — build a new handler
+ const promise = doCreateHandler(buildKey, pool, schemas, anonRole, roleName);
+ creatingHandlers.set(buildKey, promise);
+
+ try {
+ const result = await promise;
+ if (svcKeyEpoch.get(svcKey) === epoch) {
+ registerMapping(svcKey, buildKey, databaseId);
+ } else {
+ // Stale completion — a newer request for this svc_key superseded us.
+ // Defer the orphan check so coalesced followers from OTHER svc_keys
+ // have a chance to register before we decide the buildKey is orphaned.
+ queueMicrotask(() => {
+ if (getSvcKeysForBuildKey(buildKey).length === 0) {
+ evictBuildKey(buildKey);
+ }
+ });
+ }
+ return result;
+ } finally {
+ creatingHandlers.delete(buildKey);
+ }
+}
+
+async function doCreateHandler(
+ buildKey: string,
+ pool: Pool,
+ schemas: string[],
+ anonRole: string,
+ roleName: string,
+): Promise {
+ const schemaLabel = schemas.join(',') || 'unknown';
+
+ log.info(`Building handler buildKey=${buildKey} schemas=${schemaLabel}`);
+
+ const preset = presetBuilder!(pool, schemas, anonRole, roleName);
+ const pgl = postgraphile(preset);
+ const serv = pgl.createServ(grafserv);
+
+ const handler = express();
+ const httpServer = createServer(handler);
+ await serv.addTo(handler, httpServer);
+ await serv.ready();
+
+ const tenant: TenantInstance = {
+ buildKey,
+ handler,
+ schemas,
+ pgl,
+ httpServer,
+ createdAt: Date.now(),
+ lastUsedAt: Date.now(),
+ };
+
+ handlerCache.set(buildKey, tenant);
+
+ log.info(`Handler created buildKey=${buildKey} schemas=${schemaLabel}`);
+ return tenant;
+}
+
+/**
+ * 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 {
+ const buildKey = svcKeyToBuildKey.get(svcKey);
+ if (!buildKey) return;
+
+ evictBuildKey(buildKey);
+ log.debug(`Flushed via svc_key=${svcKey} → buildKey=${buildKey}`);
+}
+
+/**
+ * Flush all handlers associated with a databaseId.
+ */
+export function flushByDatabaseId(databaseId: string): void {
+ const buildKeys = databaseIdToBuildKeys.get(databaseId);
+ if (!buildKeys || buildKeys.size === 0) return;
+
+ // Copy to avoid mutation during iteration
+ const keysToEvict = [...buildKeys];
+ for (const buildKey of keysToEvict) {
+ evictBuildKey(buildKey);
+ }
+
+ // Clean up the databaseId entry (evictBuildKey already removes individual keys)
+ databaseIdToBuildKeys.delete(databaseId);
+
+ log.debug(`Flushed ${keysToEvict.length} handler(s) for databaseId=${databaseId}`);
+}
+
+async function disposeTenant(tenant: TenantInstance): Promise {
+ try {
+ if (tenant.httpServer?.listening) {
+ await new Promise((resolve) => {
+ tenant.httpServer.close(() => resolve());
+ });
+ }
+ if (tenant.pgl) {
+ await tenant.pgl.release();
+ }
+ } catch (err) {
+ log.error(`Error disposing handler buildKey=${tenant.buildKey}:`, err);
+ }
+}
+
+/**
+ * Get diagnostic stats for the multi-tenancy cache system.
+ */
+export function getMultiTenancyCacheStats(): MultiTenancyCacheStats {
+ return {
+ handlerCacheSize: handlerCache.size,
+ svcKeyMappings: svcKeyToBuildKey.size,
+ databaseIdMappings: databaseIdToBuildKeys.size,
+ inflightCreations: creatingHandlers.size,
+ };
+}
+
+/**
+ * Release all resources — handler cache, indexes, and in-flight trackers.
+ */
+export async function shutdownMultiTenancyCache(): Promise {
+ log.info('Shutting down multi-tenancy cache...');
+
+ // Dispose all cached handlers
+ const disposals: Promise[] = [];
+ for (const handler of handlerCache.values()) {
+ disposals.push(disposeTenant(handler));
+ }
+ await Promise.allSettled(disposals);
+
+ // Clear all state
+ handlerCache.clear();
+ svcKeyToBuildKey.clear();
+ databaseIdToBuildKeys.clear();
+ creatingHandlers.clear();
+ svcKeyEpoch.clear();
+ presetBuilder = null;
+
+ log.info('Multi-tenancy cache shutdown complete');
+}
diff --git a/graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts b/graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts
new file mode 100644
index 0000000000..8b669096d9
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts
@@ -0,0 +1,175 @@
+import crypto from 'node:crypto';
+
+/**
+ * Minimal introspection types — just enough for fingerprinting.
+ * These mirror pg-introspection's types but only include
+ * the fields needed for structural comparison.
+ */
+
+export interface IntrospectionNamespace {
+ nspname: string;
+ oid: string;
+}
+
+export interface IntrospectionClass {
+ relname: string;
+ relnamespace: string;
+ relkind: string;
+}
+
+export interface IntrospectionAttribute {
+ attrelid: string;
+ attname: string;
+ atttypid: string;
+ attnum: number;
+ attnotnull: boolean;
+}
+
+export interface IntrospectionConstraint {
+ conname: string;
+ conrelid: string;
+ contype: string;
+ conkey: number[] | null;
+ confrelid: string | null;
+ confkey: number[] | null;
+}
+
+export interface IntrospectionType {
+ typname: string;
+ typnamespace: string;
+ typtype: string;
+}
+
+export interface IntrospectionProc {
+ proname: string;
+ pronamespace: string;
+ proargtypes: string;
+ prorettype: string;
+ provolatile: string;
+}
+
+export interface MinimalIntrospection {
+ namespaces: IntrospectionNamespace[];
+ classes: IntrospectionClass[];
+ attributes: IntrospectionAttribute[];
+ constraints: IntrospectionConstraint[];
+ types: IntrospectionType[];
+ procs: IntrospectionProc[];
+}
+
+/**
+ * Compute a schema-name-agnostic structural fingerprint.
+ *
+ * Included in fingerprint: table names, column names, data types,
+ * constraints, function signatures.
+ *
+ * Excluded: schema/namespace names, OIDs, instance-specific identifiers.
+ * This ensures t_1_services_public.apis and t_2_services_public.apis
+ * produce the same fingerprint.
+ *
+ * @param introspection - Parsed introspection result
+ * @param schemaNames - Optional list of schema names to filter by
+ * @returns SHA-256 hex string
+ */
+export function getSchemaFingerprint(
+ introspection: MinimalIntrospection,
+ schemaNames?: string[],
+): string {
+ const schemaOids = new Set();
+
+ if (schemaNames && schemaNames.length > 0) {
+ const nameSet = new Set(schemaNames);
+ for (const ns of introspection.namespaces) {
+ if (nameSet.has(ns.nspname)) {
+ schemaOids.add(ns.oid);
+ }
+ }
+ } else {
+ for (const ns of introspection.namespaces) {
+ schemaOids.add(ns.oid);
+ }
+ }
+
+ // Filter classes to target schemas
+ const classes = introspection.classes
+ .filter((c) => schemaOids.has(c.relnamespace))
+ .sort((a, b) => a.relname.localeCompare(b.relname));
+
+ const classOids = new Set(classes.map((c) => (c as any).oid || c.relname));
+
+ // Normalize tables: name + kind (strip schema)
+ const tables = classes.map((c) => `${c.relname}:${c.relkind}`);
+
+ // Normalize columns: tableName.colName:typeOid:notNull:attNum
+ const columns = introspection.attributes
+ .filter((a) => {
+ // Find the class this attribute belongs to
+ const cls = introspection.classes.find(
+ (c) => ((c as any).oid || c.relname) === a.attrelid,
+ );
+ return cls && schemaOids.has(cls.relnamespace);
+ })
+ .sort((a, b) => {
+ if (a.attrelid !== b.attrelid) return a.attrelid.localeCompare(b.attrelid);
+ return a.attnum - b.attnum;
+ })
+ .map((a) => {
+ const cls = introspection.classes.find(
+ (c) => ((c as any).oid || c.relname) === a.attrelid,
+ );
+ const tableName = cls?.relname || a.attrelid;
+ return `${tableName}.${a.attname}:${a.atttypid}:${a.attnotnull}:${a.attnum}`;
+ });
+
+ // Normalize constraints: sorted by name, with type and key columns
+ const constraints = introspection.constraints
+ .filter((c) => {
+ const cls = introspection.classes.find(
+ (cl) => ((cl as any).oid || cl.relname) === c.conrelid,
+ );
+ return cls && schemaOids.has(cls.relnamespace);
+ })
+ .sort((a, b) => a.conname.localeCompare(b.conname))
+ .map((c) => {
+ const cls = introspection.classes.find(
+ (cl) => ((cl as any).oid || cl.relname) === c.conrelid,
+ );
+ const tableName = cls?.relname || c.conrelid;
+ const keys = c.conkey ? c.conkey.sort().join(',') : '';
+ const fkeys = c.confkey ? c.confkey.sort().join(',') : '';
+ return `${tableName}.${c.conname}:${c.contype}:${keys}:${fkeys}`;
+ });
+
+ // Normalize types: name + kind (strip namespace)
+ const types = introspection.types
+ .filter((t) => schemaOids.has(t.typnamespace))
+ .sort((a, b) => a.typname.localeCompare(b.typname))
+ .map((t) => `${t.typname}:${t.typtype}`);
+
+ // Normalize procs: name + arg types + return type + volatility (strip namespace)
+ const procs = introspection.procs
+ .filter((p) => schemaOids.has(p.pronamespace))
+ .sort((a, b) => {
+ if (a.proname !== b.proname) return a.proname.localeCompare(b.proname);
+ return a.proargtypes.localeCompare(b.proargtypes);
+ })
+ .map((p) => `${p.proname}:${p.proargtypes}:${p.prorettype}:${p.provolatile}`);
+
+ // Build canonical string and hash
+ const canonical = [
+ `tables:${tables.join('|')}`,
+ `columns:${columns.join('|')}`,
+ `constraints:${constraints.join('|')}`,
+ `types:${types.join('|')}`,
+ `procs:${procs.join('|')}`,
+ ].join('\n');
+
+ return crypto.createHash('sha256').update(canonical).digest('hex');
+}
+
+/**
+ * Compare two fingerprints for equality.
+ */
+export function fingerprintsMatch(a: string, b: string): boolean {
+ return a === b;
+}
diff --git a/graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts b/graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts
new file mode 100644
index 0000000000..32cf2ef533
--- /dev/null
+++ b/graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts
@@ -0,0 +1,124 @@
+import { Logger } from '@pgpmjs/logger';
+import type { Pool } from 'pg';
+
+const log = new Logger('introspection-query');
+
+/**
+ * Low-level introspection fetch + parse.
+ *
+ * Queries pg_catalog tables directly to get schema structure.
+ * Uses BEGIN + SET LOCAL search_path + COMMIT so the search_path
+ * never leaks to pooled connections.
+ */
+
+/**
+ * Fetch raw introspection JSON from the database.
+ *
+ * @param pool - PostgreSQL connection pool
+ * @param schemas - Schema names to introspect
+ * @returns Raw JSON string containing introspection data
+ */
+export async function fetchIntrospection(
+ pool: Pool,
+ schemas: string[],
+): Promise {
+ const client = await pool.connect();
+ try {
+ await client.query('BEGIN');
+ await client.query(`SET LOCAL search_path TO ${schemas.map((s) => `"${s}"`).join(', ')}`);
+
+ const result = await client.query(`
+ SELECT json_build_object(
+ 'namespaces', (
+ SELECT coalesce(json_agg(row_to_json(n)), '[]'::json)
+ FROM pg_catalog.pg_namespace n
+ WHERE n.nspname = ANY($1)
+ ),
+ 'classes', (
+ SELECT coalesce(json_agg(row_to_json(c)), '[]'::json)
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
+ WHERE n.nspname = ANY($1)
+ AND c.relkind IN ('r', 'v', 'm', 'f', 'p')
+ ),
+ 'attributes', (
+ SELECT coalesce(json_agg(row_to_json(a)), '[]'::json)
+ FROM pg_catalog.pg_attribute a
+ JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
+ WHERE n.nspname = ANY($1)
+ AND a.attnum > 0
+ AND NOT a.attisdropped
+ ),
+ 'constraints', (
+ SELECT coalesce(json_agg(row_to_json(co)), '[]'::json)
+ FROM pg_catalog.pg_constraint co
+ JOIN pg_catalog.pg_class c ON co.conrelid = c.oid
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
+ WHERE n.nspname = ANY($1)
+ ),
+ 'types', (
+ SELECT coalesce(json_agg(row_to_json(t)), '[]'::json)
+ FROM pg_catalog.pg_type t
+ JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
+ WHERE n.nspname = ANY($1)
+ ),
+ 'procs', (
+ SELECT coalesce(json_agg(row_to_json(p)), '[]'::json)
+ FROM pg_catalog.pg_proc p
+ JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
+ WHERE n.nspname = ANY($1)
+ )
+ ) AS introspection
+ `, [schemas]);
+
+ await client.query('COMMIT');
+
+ return JSON.stringify(result.rows[0].introspection);
+ } catch (err) {
+ await client.query('ROLLBACK').catch(() => {});
+ log.error('Introspection query failed', err);
+ throw err;
+ } finally {
+ client.release();
+ }
+}
+
+/**
+ * Parse a raw introspection JSON string into structured data.
+ *
+ * @param text - Raw JSON string from fetchIntrospection
+ * @returns Parsed introspection result
+ */
+export function parseIntrospection(text: string): import('./fingerprint').MinimalIntrospection {
+ try {
+ const data = JSON.parse(text);
+ return {
+ namespaces: data.namespaces || [],
+ classes: data.classes || [],
+ attributes: data.attributes || [],
+ constraints: data.constraints || [],
+ types: data.types || [],
+ procs: data.procs || [],
+ };
+ } catch (err) {
+ log.error('Failed to parse introspection JSON', err);
+ throw err;
+ }
+}
+
+/**
+ * Fetch and parse introspection data in one call.
+ *
+ * @param pool - PostgreSQL connection pool
+ * @param schemas - Schema names to introspect
+ * @returns Object with raw JSON string and parsed data
+ */
+export async function fetchAndParseIntrospection(
+ pool: Pool,
+ schemas: string[],
+): Promise<{ raw: string; parsed: import('./fingerprint').MinimalIntrospection }> {
+ const raw = await fetchIntrospection(pool, schemas);
+ const parsed = parseIntrospection(raw);
+ return { raw, parsed };
+}
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 2211526c42..3748debe9b 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,
} = env;
return {
@@ -50,6 +51,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) }),
},
};
};
diff --git a/graphql/server/package.json b/graphql/server/package.json
index 269d6b1034..4ad6785b82 100644
--- a/graphql/server/package.json
+++ b/graphql/server/package.json
@@ -63,6 +63,7 @@
"graphile-build-pg": "5.0.0",
"graphile-cache": "workspace:^",
"graphile-config": "1.0.0",
+ "graphile-multi-tenancy-cache": "workspace:^",
"graphile-settings": "workspace:^",
"graphile-utils": "5.0.0",
"graphql": "16.13.0",
diff --git a/graphql/server/perf/E2E_BENCHMARK_REPORT.md b/graphql/server/perf/E2E_BENCHMARK_REPORT.md
new file mode 100644
index 0000000000..dd07d50bea
--- /dev/null
+++ b/graphql/server/perf/E2E_BENCHMARK_REPORT.md
@@ -0,0 +1,155 @@
+# E2E Multi-Tenancy Cache Benchmark Report
+
+## Test Configuration
+
+| Parameter | Value |
+|---|---|
+| **Mode** | `apiIsPublic=false` (header-based routing via `X-Schemata` + `X-Database-Id`) |
+| **Tenants (k)** | 20 |
+| **Duration** | 5 minutes (300s) per mode |
+| **Workers** | 8 concurrent |
+| **Schema** | `services_public` |
+| **Server** | Constructive GraphQL server (PR #3 branch, linked Crystal PR #5) |
+| **Database** | PostgreSQL 18 via pgpm (`constructive-local` deployed) |
+| **Node.js** | `NODE_ENV=development` |
+| **Old approach GRAPHILE_CACHE_MAX** | 120 (enlarged to prevent cache eviction churn — gives old approach best-case performance) |
+
+## Query Mix
+
+| Operation | Weight | Description |
+|---|---|---|
+| `ListApis` | 40% | `{ apis(first: 10) { nodes { id name dbname isPublic } totalCount } }` |
+| `ListApps` | 20% | `{ apps(first: 10) { nodes { id name databaseId } totalCount } }` |
+| `ListDomains` | 20% | `{ domains(first: 10) { nodes { id domain subdomain } totalCount } }` |
+| `Introspection` | 10% | `{ __schema { queryType { name } mutationType { name } types { name kind } } }` |
+| `MetaQuery` | 10% | `{ _meta { tables { name schemaName fields { name } } } }` |
+
+All queries are real GraphQL HTTP requests through the full Express → PostGraphile → Grafast pipeline.
+
+## Throughput & Latency
+
+| Metric | Dedicated (Old, CACHE_MAX=120) | Multi-tenant (New) | Delta |
+|---|---|---|---|
+| **Total Queries** | 212,052 | 234,125 | **+10.4%** |
+| **Errors** | 0 | 0 | — |
+| **QPS** | 706 | 780 | **+10.5%** |
+| **p50 Latency** | 11ms | 11ms | same |
+| **p95 Latency** | 23ms | 16ms | **-30.4% (7ms faster)** |
+| **p99 Latency** | 42ms | 29ms | **-31.0% (13ms faster)** |
+
+> **Note:** The old approach was given `GRAPHILE_CACHE_MAX=120` (6× the number of tenants) to eliminate
+> cache eviction churn entirely. This represents the **best-case scenario** for the dedicated-instance
+> approach. Even with this advantage, the multi-tenancy cache still outperforms it across every metric.
+
+## Server-Side Memory
+
+Both snapshots captured from the server's `/debug/memory` endpoint before and after the 5-minute sustained load.
+
+| Metric | Dedicated (Old, CACHE_MAX=120) | Multi-tenant (New) | Delta |
+|---|---|---|---|
+| **Heap Start** | 321.8 MB | 321.8 MB | same |
+| **Heap End** | 1,597.8 MB | 656.1 MB | **-941.7 MB (-58.9%)** |
+| **Heap Growth** | +1,276.0 MB | +334.3 MB | **-941.7 MB (73.8% less growth)** |
+| **RSS Start** | 421.2 MB | 421.2 MB | same |
+| **RSS End** | 2,118.4 MB | 1,265.9 MB | **-852.5 MB (-40.2%)** |
+| **RSS Growth** | +1,697.2 MB | +844.8 MB | **-852.4 MB (50.2% less growth)** |
+| **External (end)** | 173.7 MB | 159.7 MB | -14.1 MB (-8.1%) |
+
+> **RSS** (Resident Set Size) is the total physical RAM the server process occupies, as reported by the OS.
+> It includes heap, external buffers, code segments, stacks, and shared libraries. RSS is what matters for
+> production capacity planning — it's the real memory footprint.
+
+### Cache Internals
+
+| Metric | Dedicated (Old) | Multi-tenant (New) |
+|---|---|---|
+| Graphile Cache entries | 20/120 (all tenants cached) | 0/15 (bypassed entirely) |
+| Svc Cache entries | 20/25 | 20/25 |
+| PostGraphile Builds (started) | 20 | **0** (template reuse) |
+| PostGraphile Builds (succeeded) | 20 | **0** |
+| Build Time (total) | 112ms | **0ms** |
+
+> Even with `GRAPHILE_CACHE_MAX=120` (no eviction churn), the old approach still grows **1.3 GB heap**
+> and **1.7 GB RSS** over 5 minutes — each of the 20 dedicated PostGraphile instances maintains its own
+> operation plan cache, compiled schema, and V8 closures. The new approach grows **3.8× less heap** and
+> **2× less RSS** because all 20 tenants share a single compiled template with one operation plan cache.
+
+## Cold Start (per-tenant schema build)
+
+| Metric | Dedicated (Old) | Multi-tenant (New) | Delta |
+|---|---|---|---|
+| 1st tenant | 873ms | 1,372ms | +57% (full template build) |
+| 2nd+ tenant avg | 412ms | **7ms** | **98.3% faster** |
+| Last tenant | 489ms | 5ms | **-99%** |
+
+## Analysis
+
+### Why GRAPHILE_CACHE_MAX matters for the old approach
+
+Without enlarging `GRAPHILE_CACHE_MAX`, the legacy graphile-cache uses an LRU with `max=15` entries by default. With 20 tenants, the cache constantly evicts and rebuilds PostGraphile schema instances, causing:
+
+- **1,110+ schema builds** triggered by eviction churn over 5 minutes
+- QPS drops to **~13** because requests are blocked on schema rebuilds
+- Heap peaks at **1.8 GB** and never reclaims
+- p50 latency rises to **212ms**, p99 to **2,559ms**
+
+By setting `GRAPHILE_CACHE_MAX=120`, all 20 tenants fit in cache with room to spare. This eliminates churn and gives the old approach its best-case performance (~706 QPS, 11ms p50).
+
+### Why the new approach still wins
+
+Even when the old approach runs at its best (no eviction churn), the multi-tenancy cache delivers:
+
+- **+10.5% higher QPS** (780 vs 706) — shared template means less per-request overhead
+- **30% lower tail latency** (p95: 16ms vs 23ms, p99: 29ms vs 42ms) — no per-tenant instance lookup variance
+- **98.3% faster tenant onboarding** (7ms vs 412ms avg cold start for 2nd+ tenants) — template reuse vs full schema build
+- **Zero PostGraphile builds during sustained load** — all operation plans compiled once and shared
+
+### Scaling implications
+
+At production scale (k=100+), the old approach would need `GRAPHILE_CACHE_MAX=600+` to avoid churn, consuming proportionally more memory for 100 separate PostGraphile instances. The new approach scales with O(1) templates regardless of tenant count — memory grows only for the lightweight `svcCache` entries (~few KB each).
+
+## How to Reproduce
+
+```bash
+# Start PostgreSQL
+pgpm docker start --image docker.io/constructiveio/postgres-plus:18
+eval "$(pgpm env)"
+pgpm deploy --yes --package constructive-local --recursive
+
+# Run the orchestrated benchmark (starts both modes automatically)
+cd graphql/server
+bash run-e2e-benchmark.sh 20 300 8
+# Args: K=20 tenants, DURATION=300s, WORKERS=8
+
+# Or run individual modes manually:
+# OLD mode (with enlarged cache):
+GRAPHILE_CACHE_MAX=120 MODE=old K=20 DURATION=300 WORKERS=8 npx ts-node e2e-benchmark.ts
+
+# NEW mode (start server with USE_MULTI_TENANCY_CACHE=true):
+MODE=new K=20 DURATION=300 WORKERS=8 npx ts-node e2e-benchmark.ts
+```
+
+## Files
+
+- `graphql/server/e2e-benchmark.ts` — Benchmark script (real GraphQL HTTP requests)
+- `graphql/server/run-e2e-benchmark.sh` — Orchestrator (runs both modes, compares results)
+- `graphql/server/perf/E2E_BENCHMARK_REPORT.md` — This report
+- `graphql/server/perf/results/e2e-benchmark-old-k20.json` — Raw OLD mode results
+- `graphql/server/perf/results/e2e-benchmark-new-k20.json` — Raw NEW mode results
+
+### Migrated perf framework scripts (from attachment)
+
+- `graphql/server/perf/common.mjs` — Core utilities (HTTP helpers, CLI arg parsing, file I/O)
+- `graphql/server/perf/phase1-preflight.mjs` — Preflight validation (server health, token pool, keyspace)
+- `graphql/server/perf/phase2-load.mjs` — Sustained load generation with cache activity tracking
+- `graphql/server/perf/run-k-sweep.mjs` — K-value sweep orchestrator
+- `graphql/server/perf/run-comparison.sh` — Bash orchestrator for old vs new comparison
+- `graphql/server/perf/run-test-spec.mjs` — Phase orchestrator (spawns phase1 + phase2)
+- `graphql/server/perf/build-token-pool.mjs` — Token pool builder
+- `graphql/server/perf/build-keyspace-profiles.mjs` — Keyspace expansion
+- `graphql/server/perf/build-business-op-profiles.mjs` — Business operation profiles
+- `graphql/server/perf/seed-real-multitenant.mjs` — Real tenant provisioning
+- `graphql/server/perf/reset-business-test-data.mjs` — Test data reset
+- `graphql/server/perf/prepare-public-test-access.mjs` — Public access setup
+- `graphql/server/perf/public-test-access-lib.mjs` — RLS policy helpers
+- `graphql/server/perf/README.md` — Perf framework documentation
diff --git a/graphql/server/perf/README.md b/graphql/server/perf/README.md
new file mode 100644
index 0000000000..61407e8d59
--- /dev/null
+++ b/graphql/server/perf/README.md
@@ -0,0 +1,125 @@
+# Constructive GraphQL Server — Performance Test Suite
+
+Migrated from the standalone perf framework into the Constructive monorepo.
+Runs real GraphQL HTTP requests through the full Express → PostGraphile → Grafast pipeline.
+
+## Quick Start
+
+### E2E Comparison (Old vs New)
+
+```bash
+# 5-minute debug run, k=20 tenants, 8 workers
+# Automatically enlarges GRAPHILE_CACHE_MAX for old approach
+bash graphql/server/perf/run-comparison.sh --k 20 --duration 300 --workers 8
+```
+
+### Individual Scripts
+
+```bash
+# Phase 1: Preflight checks
+node graphql/server/perf/phase1-preflight.mjs --run-dir /tmp/constructive-perf/run1
+
+# Phase 2: Sustained load
+node graphql/server/perf/phase2-load.mjs --run-dir /tmp/constructive-perf/run1 \
+ --workers 8 --duration-seconds 300
+
+# K-sweep: Multiple k-values with server restart per tier
+node graphql/server/perf/run-k-sweep.mjs --k-values 10,20 \
+ --duration-seconds 300 --workers 8 --api-is-public false
+
+# Seed real tenants
+node graphql/server/perf/seed-real-multitenant.mjs --tenant-count 20
+
+# Build token pool from credentials
+node graphql/server/perf/build-token-pool.mjs --run-dir /tmp/constructive-perf/run1
+
+# Build business operation profiles
+node graphql/server/perf/build-business-op-profiles.mjs --run-dir /tmp/constructive-perf/run1
+
+# Reset test data
+node graphql/server/perf/reset-business-test-data.mjs --run-dir /tmp/constructive-perf/run1
+```
+
+## Architecture
+
+### Phases
+
+| Phase | Script | Purpose |
+|-------|--------|---------|
+| Preflight | `phase1-preflight.mjs` | Health checks, token pool building, keyspace expansion |
+| Tech Validate | `phase1-tech-validate-dbpm.mjs` | DBPM tenant provisioning, schema creation validation |
+| Load | `phase2-load.mjs` | Concurrent GraphQL workers, cache activity tracking, fail-fast guards |
+| K-Sweep | `run-k-sweep.mjs` | Multi-k orchestration with server restart per tier |
+| Comparison | `run-comparison.sh` | Old vs New side-by-side with enlarged `GRAPHILE_CACHE_MAX` for old mode |
+
+### Supporting Scripts
+
+| Script | Purpose |
+|--------|---------|
+| `common.mjs` | Shared utilities (HTTP helpers, CLI arg parsing, file I/O) |
+| `seed-real-multitenant.mjs` | Creates real tenants (orgs, users, memberships) |
+| `build-token-pool.mjs` | Authenticates credentials, generates bearer tokens |
+| `build-keyspace-profiles.mjs` | Expands route keyspace via schema combinations |
+| `build-business-op-profiles.mjs` | Builds business operation profiles from manifest |
+| `reset-business-test-data.mjs` | Truncates business test tables between runs |
+| `prepare-public-test-access.mjs` | Sets up public lane access (grants, RLS policies) |
+| `public-test-access-lib.mjs` | Shared library for public access preparation |
+| `run-test-spec.mjs` | Phase orchestrator wrapper |
+
+## Key Configuration
+
+### GRAPHILE_CACHE_MAX
+
+**CRITICAL**: When testing the old (dedicated) approach, always enlarge `GRAPHILE_CACHE_MAX`
+to prevent cache eviction churn that would artificially penalize the old strategy.
+
+The `run-comparison.sh` script automatically sets this to `max(100, k * 6)`.
+
+For manual runs:
+```bash
+# Old mode: enlarge cache to hold all tenant instances
+export GRAPHILE_CACHE_MAX=120 # for k=20 tenants × ~3 endpoints
+
+# New mode: no need to set (multi-tenancy cache bypasses the graphile LRU cache)
+```
+
+### Environment Variables
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `PGHOST` | `localhost` | PostgreSQL host |
+| `PGPORT` | `5432` | PostgreSQL port |
+| `PGUSER` | `postgres` | PostgreSQL user |
+| `PGPASSWORD` | `password` | PostgreSQL password |
+| `PGDATABASE` | `postgres` | PostgreSQL database |
+| `API_IS_PUBLIC` | `false` | Routing mode (false = header-based private routing) |
+| `USE_MULTI_TENANCY_CACHE` | unset | Enable multi-tenancy cache (new approach) |
+| `GRAPHILE_CACHE_MAX` | `15` | Max graphile cache entries (enlarge for old approach!) |
+
+## Output
+
+All results are written to the run directory (`/tmp/constructive-perf//`):
+
+```
+/
+├── data/ # Profiles, tokens, manifests
+├── logs/ # Server logs, sampler output
+│ ├── sampler/ # Debug sampler JSONL files
+│ └── heap/ # Heap snapshots
+├── reports/ # Summary reports, analysis output
+└── tmp-scripts/ # Temporary script artifacts
+```
+
+## Adaptations from Original Framework
+
+- `DEFAULT_TMP_ROOT` → `/tmp/constructive-perf` (was macOS user path)
+- Server start via `npx ts-node src/run.ts` (was `packages/cli/dist/index.js`)
+- Script paths adjusted for `graphql/server/perf/` location
+- `capture-heap-snapshot.mjs` and `analyze-debug-logs.mjs` referenced from `../scripts/`
+
+## TODO
+
+Once Crystal PR #5 is published:
+- Remove `link:` overrides from package.json
+- Replace compat shim with direct import from `@dataplan/pg`
+- Re-run full k-sweep comparison with published packages
diff --git a/graphql/server/perf/build-business-op-profiles.mjs b/graphql/server/perf/build-business-op-profiles.mjs
new file mode 100644
index 0000000000..89b5e44d6e
--- /dev/null
+++ b/graphql/server/perf/build-business-op-profiles.mjs
@@ -0,0 +1,483 @@
+#!/usr/bin/env node
+
+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.mjs';
+
+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', 'app').trim();
+const publicSubdomainPrefix = getArgValue(args, '--public-subdomain-prefix', 'app-public-').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/build-keyspace-profiles.mjs b/graphql/server/perf/build-keyspace-profiles.mjs
new file mode 100644
index 0000000000..528f3fd164
--- /dev/null
+++ b/graphql/server/perf/build-keyspace-profiles.mjs
@@ -0,0 +1,333 @@
+#!/usr/bin/env node
+
+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.mjs';
+
+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/build-token-pool.mjs b/graphql/server/perf/build-token-pool.mjs
new file mode 100644
index 0000000000..b779987d62
--- /dev/null
+++ b/graphql/server/perf/build-token-pool.mjs
@@ -0,0 +1,161 @@
+#!/usr/bin/env node
+
+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.mjs';
+
+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/common.mjs b/graphql/server/perf/common.mjs
new file mode 100644
index 0000000000..b8db375523
--- /dev/null
+++ b/graphql/server/perf/common.mjs
@@ -0,0 +1,231 @@
+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/e2e-benchmark.ts b/graphql/server/perf/e2e-benchmark.ts
new file mode 100644
index 0000000000..ad15757135
--- /dev/null
+++ b/graphql/server/perf/e2e-benchmark.ts
@@ -0,0 +1,398 @@
+#!/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'
+
+// 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);
+ if (parsed.heapUsed) return parsed.heapUsed / 1024 / 1024;
+ } catch {
+ // fallback
+ }
+ return process.memoryUsage().heapUsed / 1024 / 1024;
+}
+
+// ─── Tenant Profiles ────────────────────────────────────────────────────────
+function buildTenantProfiles(k: number): TenantProfile[] {
+ const profiles: TenantProfile[] = [];
+ 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[] {
+ 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('='.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;
+ allLatencies.push(...r.latencies);
+ 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/phase1-preflight.mjs b/graphql/server/perf/phase1-preflight.mjs
new file mode 100644
index 0000000000..aeca1ecc04
--- /dev/null
+++ b/graphql/server/perf/phase1-preflight.mjs
@@ -0,0 +1,761 @@
+#!/usr/bin/env node
+
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import { spawn } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+import { Pool } from 'pg';
+
+import {
+ DEFAULT_BASE_URL,
+ DEFAULT_TMP_ROOT,
+ dedupeBy,
+ ensureRunDirs,
+ getArgValue,
+ getJson,
+ hasFlag,
+ makeRunId,
+ postJson,
+ stableStringify,
+ writeJson,
+} from './common.mjs';
+
+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 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 = fileURLToPath(new URL('./build-token-pool.mjs', import.meta.url));
+
+ return await new Promise((resolve) => {
+ const child = spawn(
+ process.execPath,
+ [
+ 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 = fileURLToPath(new URL('./phase1-tech-validate-dbpm.mjs', import.meta.url));
+
+ return await new Promise((resolve) => {
+ const child = spawn(
+ process.execPath,
+ [
+ scriptPath,
+ '--run-dir',
+ runDir,
+ '--base-url',
+ baseUrl,
+ '--tenant-count',
+ String(dbpmTenantCount),
+ '--user-password',
+ dbpmUserPassword,
+ '--user-prefix',
+ dbpmUserPrefix,
+ '--shape-variants',
+ String(dbpmShapeVariants),
+ ],
+ { 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 = fileURLToPath(new URL('./build-business-op-profiles.mjs', import.meta.url));
+
+ return await new Promise((resolve) => {
+ const child = spawn(
+ process.execPath,
+ [
+ scriptPath,
+ '--run-dir',
+ runDir,
+ '--base-url',
+ baseUrl,
+ '--manifest',
+ businessManifestPath,
+ '--tokens',
+ tokenOutputPath,
+ '--output',
+ businessProfilesOutputPath,
+ ],
+ { 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 = fileURLToPath(new URL('./build-keyspace-profiles.mjs', import.meta.url));
+
+ const cmd = [
+ 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(process.execPath, 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/phase1-tech-validate-dbpm.mjs b/graphql/server/perf/phase1-tech-validate-dbpm.mjs
new file mode 100644
index 0000000000..9b2f4f20a7
--- /dev/null
+++ b/graphql/server/perf/phase1-tech-validate-dbpm.mjs
@@ -0,0 +1,768 @@
+#!/usr/bin/env node
+
+import path from 'node:path';
+import { Pool } from 'pg';
+
+import {
+ DEFAULT_BASE_URL,
+ DEFAULT_TMP_ROOT,
+ ensureRunDirs,
+ getArgValue,
+ makeRunId,
+ postJson,
+ sleep,
+ writeJson,
+} from './common.mjs';
+
+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 routeHeaders = {
+ Host: routeHost,
+ 'X-Api-Name': privateApiName,
+ 'X-Database-Id': privateDatabaseId,
+};
+
+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)
+// ---------------------------------------------------------------------------
+
+/**
+ * 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: [
+ { name: 'label', type: 'text' },
+ { name: 'priority', type: 'integer' },
+ ],
+ },
+ ],
+ },
+ // Group 2: extra table with 3 columns (metrics)
+ {
+ tables: [
+ {
+ suffix: 'metrics',
+ fields: [
+ { name: 'value', type: 'numeric' },
+ { name: 'recorded_at', type: 'timestamptz' },
+ { name: 'active', type: '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
+ nodeType
+ outFields
+ }
+ }
+}
+`;
+
+const gql = async ({ query, variables, headers = {}, timeoutMs = 30000 }) => {
+ return await postJson({
+ url: `${baseUrl}/graphql`,
+ headers: {
+ ...routeHeaders,
+ ...headers,
+ },
+ payload: { query, variables },
+ timeoutMs,
+ });
+};
+
+const firstError = (response) => response.json?.errors?.[0]?.message || response.error || 'unknown';
+
+const signUpOrSignInUser = async ({ email, password }) => {
+ const signUpRes = await gql({
+ query: signUpMutation,
+ 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,
+ 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,
+ nodeType: 'DataId',
+ fields: [{ name: 'note', type: '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,
+ nodeType: '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,
+ nodeType: secureTable.nodeType,
+ 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,
+ routeHeaders,
+ 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: routeHost,
+ apiName: privateApiName,
+ 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/phase2-load.mjs b/graphql/server/perf/phase2-load.mjs
new file mode 100644
index 0000000000..60d5712c26
--- /dev/null
+++ b/graphql/server/perf/phase2-load.mjs
@@ -0,0 +1,1414 @@
+#!/usr/bin/env node
+
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import { spawn } from 'node:child_process';
+
+import {
+ DEFAULT_BASE_URL,
+ DEFAULT_TMP_ROOT,
+ ensureRunDirs,
+ getArgValue,
+ getJson,
+ parseIntArg,
+ postJson,
+ sleep,
+ writeJson,
+} from './common.mjs';
+import {
+ buildTargetsFromProfiles,
+ ensurePublicAccessForTargets,
+ getUnsafeTargets,
+} from './public-test-access-lib.mjs';
+
+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 = await ensureRunDirs(runDir);
+
+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 cacheActivityDelta = (start, end) => {
+ if (!start || !end) {
+ return null;
+ }
+
+ 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 : [];
+ 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 ?? 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}(condition:{id:$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 result = await postJson({
+ url: resolveProfileGraphqlUrl(profile),
+ headers: profile.headers ?? {},
+ payload: { query: '{ __typename }' },
+ timeoutMs: 15000,
+ });
+
+ const hasGraphQLErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0;
+ const ok = result.ok && !hasGraphQLErrors && result.json?.data?.__typename === 'Query';
+
+ 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(path.dirname(new URL(import.meta.url).pathname), '..', '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(path.dirname(new URL(import.meta.url).pathname), '..', '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/prepare-public-test-access.mjs b/graphql/server/perf/prepare-public-test-access.mjs
new file mode 100644
index 0000000000..11046e605b
--- /dev/null
+++ b/graphql/server/perf/prepare-public-test-access.mjs
@@ -0,0 +1,129 @@
+#!/usr/bin/env node
+
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+import {
+ DEFAULT_TMP_ROOT,
+ ensureRunDirs,
+ getArgValue,
+ hasFlag,
+ makeRunId,
+ writeJson,
+} from './common.mjs';
+import {
+ buildTargetsFromProfiles,
+ ensurePublicAccessForTargets,
+ extractProfiles,
+ getUnsafeTargets,
+} from './public-test-access-lib.mjs';
+
+const args = process.argv.slice(2);
+
+const runId = getArgValue(args, '--run-id', makeRunId('graphile-cache-public-access'));
+const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
+const profilesPath = path.resolve(
+ getArgValue(args, '--profiles', path.join(runDir, 'data', 'business-op-profiles.public.json')),
+);
+const dryRun = hasFlag(args, '--dry-run');
+const allowNonPerfSchema = hasFlag(args, '--allow-non-perf-schema');
+const tag = getArgValue(args, '--tag', '').trim();
+const publicRole = getArgValue(args, '--public-role', 'authenticated').trim();
+const publicReadRole = getArgValue(args, '--public-read-role', 'anonymous').trim();
+
+if (!publicRole) {
+ throw new Error('--public-role cannot be empty');
+}
+
+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 readJson = async (filePath) => {
+ const raw = await fs.readFile(filePath, 'utf8');
+ return JSON.parse(raw);
+};
+
+const main = async () => {
+ 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,
+ publicRole,
+ publicReadRole,
+ });
+
+ const report = {
+ createdAt: new Date().toISOString(),
+ runDir,
+ profilesPath,
+ options: {
+ 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;
+ }
+};
+
+main().catch((error) => {
+ console.error(error instanceof Error ? error.stack : String(error));
+ process.exit(1);
+});
diff --git a/graphql/server/perf/public-test-access-lib.mjs b/graphql/server/perf/public-test-access-lib.mjs
new file mode 100644
index 0000000000..354ad9f526
--- /dev/null
+++ b/graphql/server/perf/public-test-access-lib.mjs
@@ -0,0 +1,158 @@
+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',
+};
+
+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 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(
+ POLICY_NAMES.select,
+ `create policy ${quoteIdent(POLICY_NAMES.select)} on ${qualified} for select to ${publicRoleIdent} using (true);`,
+ );
+ await maybeCreatePolicy(
+ POLICY_NAMES.insert,
+ `create policy ${quoteIdent(POLICY_NAMES.insert)} on ${qualified} for insert to ${publicRoleIdent} with check (true);`,
+ );
+ await maybeCreatePolicy(
+ POLICY_NAMES.update,
+ `create policy ${quoteIdent(POLICY_NAMES.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/reset-business-test-data.mjs b/graphql/server/perf/reset-business-test-data.mjs
new file mode 100644
index 0000000000..e51bc85731
--- /dev/null
+++ b/graphql/server/perf/reset-business-test-data.mjs
@@ -0,0 +1,187 @@
+#!/usr/bin/env node
+
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import { Pool } from 'pg';
+
+import {
+ DEFAULT_TMP_ROOT,
+ ensureRunDirs,
+ getArgValue,
+ hasFlag,
+ makeRunId,
+ writeJson,
+} from './common.mjs';
+import {
+ buildTargetsFromProfiles,
+ ensurePublicAccessForTargets,
+ extractProfiles,
+ getUnsafeTargets,
+} from './public-test-access-lib.mjs';
+
+const args = process.argv.slice(2);
+
+const runId = getArgValue(args, '--run-id', makeRunId('graphile-cache-reset'));
+const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
+const profilesPath = path.resolve(
+ getArgValue(args, '--profiles', path.join(runDir, 'data', 'business-op-profiles.json')),
+);
+const dryRun = hasFlag(args, '--dry-run');
+const allowNonPerfSchema = hasFlag(args, '--allow-non-perf-schema');
+const ensurePublicTestAccess = hasFlag(args, '--ensure-public-test-access');
+const publicRole = getArgValue(args, '--public-role', 'authenticated');
+const publicReadRole = getArgValue(args, '--public-read-role', 'anonymous');
+const tag = getArgValue(args, '--tag', '').trim();
+
+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 quoteIdent = (value) => `"${String(value).replace(/"/g, '""')}"`;
+
+const readJson = async (filePath) => {
+ const raw = await fs.readFile(filePath, 'utf8');
+ return JSON.parse(raw);
+};
+
+const main = async () => {
+ 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 = [];
+ const executed = [];
+ 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 (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 = dryRun
+ ? targets
+ : executed.map((entry) => ({
+ schemaName: entry.schemaName,
+ tableName: entry.tableName,
+ databaseId: entry.databaseId ?? null,
+ profileKey: entry.profileKey ?? null,
+ }));
+ const accessResult = ensurePublicTestAccess
+ ? await ensurePublicAccessForTargets({
+ targets: accessTargets,
+ pgConfig,
+ dryRun,
+ publicRole,
+ publicReadRole,
+ })
+ : { prepared: [], failures: [] };
+ const accessPrepared = accessResult.prepared;
+ const accessFailures = accessResult.failures;
+
+ const failures = [...truncateFailures, ...accessFailures];
+
+ 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,
+ allowNonPerfSchema,
+ ensurePublicTestAccess,
+ publicRole,
+ publicReadRole: publicReadRole || null,
+ tag: tag || null,
+ },
+ totals: {
+ profileCount: profiles.length,
+ targetCount: targets.length,
+ truncatedCount: executed.length,
+ accessPreparedCount: accessPrepared.length,
+ failureCount: failures.length,
+ },
+ targets,
+ executed,
+ accessPrepared,
+ truncateFailures,
+ accessFailures,
+ 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;
+ }
+};
+
+main().catch((error) => {
+ console.error(error instanceof Error ? error.stack : String(error));
+ process.exit(1);
+});
diff --git a/graphql/server/perf/run-comparison.sh b/graphql/server/perf/run-comparison.sh
new file mode 100755
index 0000000000..35cd8bd5fd
--- /dev/null
+++ b/graphql/server/perf/run-comparison.sh
@@ -0,0 +1,245 @@
+#!/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)}"
+
+# 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:-postgres}"
+export NODE_ENV=development
+export GRAPHILE_ENV=development
+export GRAPHQL_OBSERVABILITY_ENABLED=true
+export API_IS_PUBLIC=false
+
+echo "=============================================================="
+echo "E2E Multi-Tenancy Comparison (Perf Framework)"
+echo " K=$K tenants, Duration=${DURATION}s, Workers=$WORKERS"
+echo " Old approach GRAPHILE_CACHE_MAX=$OLD_CACHE_MAX"
+echo " Run dir: $RUN_DIR"
+echo "=============================================================="
+
+mkdir -p "$RUN_DIR"
+
+kill_server() {
+ fuser -k ${SERVER_PORT}/tcp 2>/dev/null || true
+ sleep 2
+}
+
+wait_for_server() {
+ local max_wait=90
+ local waited=0
+ echo -n " Waiting for server on port $SERVER_PORT..."
+ while ! curl -sf "${BASE_URL}/debug/memory" >/dev/null 2>&1; do
+ sleep 1
+ waited=$((waited + 1))
+ if [ $waited -ge $max_wait ]; then
+ echo " TIMEOUT after ${max_wait}s"
+ return 1
+ fi
+ echo -n "."
+ done
+ 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 (shared templates)"
+ 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}"
+
+ echo ""
+ echo "=============================================================="
+ echo "Running ${mode^^} 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^^} mode complete."
+}
+
+# Capture server memory snapshot
+capture_memory() {
+ local label="$1"
+ local outfile="$RUN_DIR/memory-${label}.json"
+ curl -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 shared templates)
+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..722430b7d6
--- /dev/null
+++ b/graphql/server/perf/run-e2e-benchmark.sh
@@ -0,0 +1,111 @@
+#!/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}"
+
+# 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:-postgres}"
+export NODE_ENV=development
+export GRAPHILE_ENV=development
+export GRAPHQL_OBSERVABILITY_ENABLED=true
+export API_IS_PUBLIC=false
+
+echo "=============================================================="
+echo "E2E Multi-Tenancy Benchmark (Single Mode)"
+echo " Mode=$MODE, K=$K tenants, Duration=${DURATION}s, Workers=$WORKERS"
+echo "=============================================================="
+
+kill_server() {
+ fuser -k ${SERVER_PORT}/tcp 2>/dev/null || true
+ sleep 2
+}
+
+wait_for_server() {
+ local max_wait=120
+ local waited=0
+ echo -n " Waiting for server on port $SERVER_PORT..."
+ while ! curl -sf "${BASE_URL}/debug/memory" >/dev/null 2>&1; do
+ sleep 1
+ waited=$((waited + 1))
+ if [ $waited -ge $max_wait ]; then
+ echo " TIMEOUT after ${max_wait}s"
+ return 1
+ fi
+ echo -n "."
+ done
+ echo " ready (${waited}s)"
+}
+
+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 (shared templates)"
+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^^} 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 -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-k-sweep.mjs b/graphql/server/perf/run-k-sweep.mjs
new file mode 100644
index 0000000000..1fa054b70e
--- /dev/null
+++ b/graphql/server/perf/run-k-sweep.mjs
@@ -0,0 +1,713 @@
+#!/usr/bin/env node
+
+import fs from 'node:fs';
+import fsp from 'node:fs/promises';
+import path from 'node:path';
+import { spawn, execFile } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+
+import {
+ DEFAULT_BASE_URL,
+ DEFAULT_TMP_ROOT,
+ ensureRunDirs,
+ getArgValue,
+ hasFlag,
+ makeRunId,
+ postJson,
+ writeJson,
+} from './common.mjs';
+
+const args = process.argv.slice(2);
+
+const runId = getArgValue(args, '--run-id', makeRunId('graphile-cache-k-sweep'));
+const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
+const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL);
+const profilesPath = path.resolve(
+ getArgValue(args, '--profiles', path.join(runDir, 'data', 'business-op-profiles.json')),
+);
+
+const workers = Number.parseInt(getArgValue(args, '--workers', '16'), 10);
+const durationSeconds = Number.parseInt(getArgValue(args, '--duration-seconds', '600'), 10);
+const idleSeconds = Number.parseInt(getArgValue(args, '--idle-seconds', '120'), 10);
+const minTenantCount = Number.parseInt(getArgValue(args, '--min-tenant-count', '10'), 10);
+const churnRatio = getArgValue(args, '--churn-ratio', '0.4');
+const churnWarmSeconds = getArgValue(args, '--churn-warm-seconds', '120');
+const churnCoolSeconds = getArgValue(args, '--churn-cool-seconds', '240');
+const churnCohorts = getArgValue(args, '--churn-cohorts', '2');
+const kValuesRaw = getArgValue(args, '--k-values', '3,7');
+const continueOnError = hasFlag(args, '--continue-on-error');
+const apiIsPublicRaw = getArgValue(args, '--api-is-public', 'false').trim().toLowerCase();
+const routingMode = getArgValue(
+ args,
+ '--routing-mode',
+ apiIsPublicRaw === 'true' ? 'public' : 'private',
+)
+ .trim()
+ .toLowerCase();
+const tierMode = getArgValue(
+ args,
+ '--tier-mode',
+ routingMode === 'public' ? 'active-tenants' : 'keyspace',
+)
+ .trim()
+ .toLowerCase();
+const keyspaceMode = getArgValue(
+ args,
+ '--keyspace-mode',
+ tierMode === 'keyspace' ? 'auto' : 'none',
+)
+ .trim()
+ .toLowerCase();
+const publicRole = getArgValue(args, '--public-role', 'authenticated').trim();
+const publicReadRole = getArgValue(args, '--public-read-role', 'anonymous').trim();
+const ensurePublicTestAccess = hasFlag(args, '--ensure-public-test-access') || apiIsPublicRaw === 'true';
+
+const servicePort = Number.parseInt(getArgValue(args, '--service-port', '3000'), 10);
+const serviceOrigin = getArgValue(args, '--service-origin', '*');
+const serviceReadyTimeoutMs = Number.parseInt(getArgValue(args, '--service-ready-timeout-ms', '45000'), 10);
+const routeProbeTimeoutMs = Number.parseInt(getArgValue(args, '--route-probe-timeout-ms', '15000'), 10);
+const samplerMemoryIntervalMs = getArgValue(args, '--sampler-memory-interval-ms', '10000');
+const samplerDbIntervalMs = getArgValue(args, '--sampler-db-interval-ms', '30000');
+const graphileCacheMaxRaw = getArgValue(args, '--graphile-cache-max', process.env.GRAPHILE_CACHE_MAX || '').trim();
+const graphileSchemaWaitTimeMsRaw = getArgValue(
+ args,
+ '--graphile-schema-wait-time-ms',
+ process.env.GRAPHILE_SCHEMA_WAIT_TIME_MS || '',
+).trim();
+const prewarmDisabled = hasFlag(args, '--disable-prewarm');
+const prewarmSampleSize = Number.parseInt(getArgValue(args, '--prewarm-sample-size', '0'), 10);
+const prewarmConcurrency = Number.parseInt(getArgValue(args, '--prewarm-concurrency', '6'), 10);
+const prewarmTimeoutMs = Number.parseInt(getArgValue(args, '--prewarm-timeout-ms', '30000'), 10);
+const prewarmMaxFailures = Number.parseInt(getArgValue(args, '--prewarm-max-failures', '0'), 10);
+const maxOldSpaceSizeMb = Math.max(
+ 1024,
+ Number.parseInt(getArgValue(args, '--max-old-space-size-mb', '15360'), 10) || 15360,
+);
+const nodeOptionsRaw = getArgValue(
+ args,
+ '--node-options',
+ process.env.NODE_OPTIONS || '--heapsnapshot-signal=SIGUSR2 --expose-gc',
+);
+
+const ensureMaxOldSpaceSize = (options, sizeMb) => {
+ const text = String(options || '').trim();
+ const hasMaxOldSpace = /--max-old-space-size(?:=|\s+)/.test(text);
+ if (hasMaxOldSpace) {
+ return text;
+ }
+ return `--max-old-space-size=${sizeMb}${text.length > 0 ? ` ${text}` : ''}`;
+};
+
+const nodeOptions = ensureMaxOldSpaceSize(nodeOptionsRaw, maxOldSpaceSizeMb);
+const graphileCacheMax =
+ graphileCacheMaxRaw.length > 0 ? Number.parseInt(graphileCacheMaxRaw, 10) : null;
+const graphileSchemaWaitTimeMs =
+ graphileSchemaWaitTimeMsRaw.length > 0 ? Number.parseInt(graphileSchemaWaitTimeMsRaw, 10) : null;
+
+if (apiIsPublicRaw !== 'true' && apiIsPublicRaw !== 'false') {
+ throw new Error(`Invalid --api-is-public=${apiIsPublicRaw}; expected true|false`);
+}
+const apiIsPublic = apiIsPublicRaw === 'true';
+
+const VALID_ROUTING_MODES = new Set(['private', 'public']);
+if (!VALID_ROUTING_MODES.has(routingMode)) {
+ throw new Error(`Invalid --routing-mode=${routingMode}; expected private|public`);
+}
+if ((apiIsPublic && routingMode !== 'public') || (!apiIsPublic && routingMode !== 'private')) {
+ throw new Error(
+ `Inconsistent mode: --api-is-public=${apiIsPublicRaw} requires --routing-mode=${apiIsPublic ? 'public' : 'private'}`,
+ );
+}
+
+const VALID_TIER_MODES = new Set(['keyspace', 'active-tenants']);
+if (!VALID_TIER_MODES.has(tierMode)) {
+ throw new Error(`Invalid --tier-mode=${tierMode}; expected keyspace|active-tenants`);
+}
+if (routingMode === 'public' && tierMode === 'keyspace') {
+ throw new Error('Public routing does not support keyspace tier mode; use --tier-mode active-tenants');
+}
+
+const VALID_KEYSPACE_MODES = new Set(['auto', 'schemata', 'none']);
+if (!VALID_KEYSPACE_MODES.has(keyspaceMode)) {
+ throw new Error(`Invalid --keyspace-mode=${keyspaceMode}; expected auto|schemata|none`);
+}
+if (ensurePublicTestAccess && publicRole.length === 0) {
+ throw new Error('--public-role cannot be empty when --ensure-public-test-access is enabled');
+}
+if (graphileCacheMaxRaw.length > 0 && (!Number.isFinite(graphileCacheMax) || graphileCacheMax <= 0)) {
+ throw new Error(`Invalid --graphile-cache-max=${graphileCacheMaxRaw}; expected positive integer`);
+}
+if (
+ graphileSchemaWaitTimeMsRaw.length > 0 &&
+ (!Number.isFinite(graphileSchemaWaitTimeMs) || graphileSchemaWaitTimeMs <= 0)
+) {
+ throw new Error(
+ `Invalid --graphile-schema-wait-time-ms=${graphileSchemaWaitTimeMsRaw}; expected positive integer`,
+ );
+}
+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 pgConfig = {
+ host: getArgValue(args, '--pg-host', process.env.PGHOST || 'localhost'),
+ port: getArgValue(args, '--pg-port', process.env.PGPORT || '5432'),
+ 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 __filename = fileURLToPath(import.meta.url);
+const perfDir = path.dirname(__filename);
+const serverDir = path.resolve(perfDir, '..'); // graphql/server/
+const repoRoot = path.resolve(serverDir, '..', '..'); // repo root
+const phase2Script = path.join(perfDir, 'phase2-load.mjs');
+const resetScript = path.join(perfDir, 'reset-business-test-data.mjs');
+const serverRunScript = path.join(serverDir, 'src', 'run.ts');
+
+const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+const ABSOLUTE_URL_RE = /^https?:\/\//i;
+
+const parseKValues = (raw) => {
+ const values = String(raw)
+ .split(',')
+ .map((part) => Number.parseInt(part.trim(), 10))
+ .filter((value) => Number.isFinite(value) && value > 0);
+
+ if (values.length === 0) {
+ throw new Error(`No valid k values from --k-values=${raw}`);
+ }
+
+ return [...new Set(values)];
+};
+
+const readJson = async (filePath) => {
+ const raw = await fsp.readFile(filePath, 'utf8');
+ return JSON.parse(raw);
+};
+
+const extractProfiles = (payload) => {
+ if (Array.isArray(payload)) return payload;
+ if (Array.isArray(payload?.profiles)) return payload.profiles;
+ return [];
+};
+
+const isProcessAlive = (pid) => {
+ try {
+ process.kill(pid, 0);
+ return true;
+ } catch {
+ return false;
+ }
+};
+
+const getListeningPids = async (port) => {
+ return await new Promise((resolve) => {
+ execFile('lsof', ['-tiTCP:' + String(port), '-sTCP:LISTEN'], (error, stdout) => {
+ if (error) {
+ resolve([]);
+ return;
+ }
+
+ const pids = String(stdout)
+ .split('\n')
+ .map((line) => Number.parseInt(line.trim(), 10))
+ .filter((pid) => Number.isFinite(pid));
+ resolve([...new Set(pids)]);
+ });
+ });
+};
+
+const stopPortListeners = async (port) => {
+ const pids = await getListeningPids(port);
+ if (pids.length === 0) return;
+
+ for (const pid of pids) {
+ try {
+ process.kill(pid, 'SIGTERM');
+ } catch {
+ // ignore stale pid
+ }
+ }
+
+ const deadline = Date.now() + 4000;
+ while (Date.now() < deadline) {
+ const alive = pids.filter((pid) => isProcessAlive(pid));
+ if (alive.length === 0) return;
+ await sleep(200);
+ }
+
+ for (const pid of pids) {
+ if (!isProcessAlive(pid)) continue;
+ try {
+ process.kill(pid, 'SIGKILL');
+ } catch {
+ // ignore stale pid
+ }
+ }
+};
+
+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 runNodeScriptWithLog = async ({
+ scriptPath,
+ scriptArgs,
+ cwd,
+ env,
+ logPath,
+ abortOnExitProcess = null,
+ abortProcessLabel = 'watched process',
+}) => {
+ await fsp.mkdir(path.dirname(logPath), { recursive: true });
+ const logStream = fs.createWriteStream(logPath, { flags: 'a' });
+
+ return await new Promise((resolve) => {
+ const child = spawn(process.execPath, [scriptPath, ...scriptArgs], {
+ cwd,
+ env,
+ stdio: ['ignore', 'pipe', 'pipe'],
+ });
+
+ let stdout = '';
+ let stderr = '';
+ let abortedByGuard = null;
+
+ const terminateChild = (reason) => {
+ if (abortedByGuard || child.exitCode !== null || child.killed) {
+ return;
+ }
+
+ abortedByGuard = reason;
+ child.kill('SIGTERM');
+ const timer = setTimeout(() => {
+ if (child.exitCode === null && !child.killed) {
+ child.kill('SIGKILL');
+ }
+ }, 3000);
+ timer.unref?.();
+ };
+
+ let onAbortProcessExit = null;
+ if (abortOnExitProcess) {
+ onAbortProcessExit = (code, signal) => {
+ terminateChild(
+ `${abortProcessLabel} exited (code=${code ?? 'null'}, signal=${signal ?? 'null'})`,
+ );
+ };
+ abortOnExitProcess.once('exit', onAbortProcessExit);
+ }
+
+ child.stdout.on('data', (chunk) => {
+ const text = String(chunk);
+ stdout += text;
+ logStream.write(text);
+ process.stdout.write(text);
+ });
+
+ child.stderr.on('data', (chunk) => {
+ const text = String(chunk);
+ stderr += text;
+ logStream.write(text);
+ process.stderr.write(text);
+ });
+
+ child.on('close', (code) => {
+ if (abortOnExitProcess && onAbortProcessExit) {
+ abortOnExitProcess.off('exit', onAbortProcessExit);
+ }
+ logStream.end();
+ resolve({
+ code: Number.isFinite(code) ? code : -1,
+ stdout,
+ stderr,
+ abortedByGuard,
+ });
+ });
+ });
+};
+
+const waitForServerReady = async ({ url, timeoutMs }) => {
+ const deadline = Date.now() + timeoutMs;
+ let lastError = null;
+
+ while (Date.now() < deadline) {
+ try {
+ const response = await fetch(url, { method: 'GET' });
+ if (response.ok) {
+ return;
+ }
+ lastError = `status ${response.status}`;
+ } catch (error) {
+ lastError = error instanceof Error ? error.message : String(error);
+ }
+
+ await sleep(500);
+ }
+
+ throw new Error(`Server ready check timed out for ${url}; lastError=${lastError ?? 'n/a'}`);
+};
+
+const runRouteProbe = async ({ profile }) => {
+ const result = await postJson({
+ url: resolveProfileGraphqlUrl(profile),
+ headers: profile.headers || {},
+ payload: { query: '{ __typename }' },
+ timeoutMs: routeProbeTimeoutMs,
+ });
+ const hasGraphQLErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0;
+ const ok = result.ok && !hasGraphQLErrors && result.json?.data?.__typename === 'Query';
+ if (!ok) {
+ const firstError = hasGraphQLErrors ? result.json.errors[0]?.message ?? 'unknown GraphQL error' : null;
+ throw new Error(
+ `Route probe failed status=${result.status} profile=${profile.key ?? 'unknown'} msg=${result.error ?? firstError ?? 'unexpected GraphQL response'}`,
+ );
+ }
+};
+
+const stopServiceChild = async (child) => {
+ if (!child) return;
+ if (child.exitCode !== null || child.killed) return;
+
+ child.kill('SIGINT');
+
+ const closed = await new Promise((resolve) => {
+ const timer = setTimeout(() => resolve(false), 8000);
+ child.once('close', () => {
+ clearTimeout(timer);
+ resolve(true);
+ });
+ });
+
+ if (!closed && child.exitCode === null) {
+ child.kill('SIGKILL');
+ }
+};
+
+const startServiceForK = async ({ k, serviceLogPath, samplerDir, firstProfile }) => {
+ await fsp.mkdir(path.dirname(serviceLogPath), { recursive: true });
+ await fsp.mkdir(samplerDir, { recursive: true });
+ const logStream = fs.createWriteStream(serviceLogPath, { flags: 'a' });
+
+ // Use npx ts-node to start the server (Constructive repo convention)
+ const child = spawn(
+ 'npx',
+ ['ts-node', serverRunScript],
+ {
+ cwd: serverDir,
+ env: {
+ ...process.env,
+ NODE_ENV: 'development',
+ GRAPHILE_ENV: 'development',
+ GRAPHQL_OBSERVABILITY_ENABLED: 'true',
+ GRAPHQL_DEBUG_SAMPLER_ENABLED: 'true',
+ GRAPHQL_DEBUG_SAMPLER_MEMORY_INTERVAL_MS: String(samplerMemoryIntervalMs),
+ GRAPHQL_DEBUG_SAMPLER_DB_INTERVAL_MS: String(samplerDbIntervalMs),
+ GRAPHQL_DEBUG_SAMPLER_DIR: samplerDir,
+ API_IS_PUBLIC: apiIsPublic ? 'true' : 'false',
+ SERVER_PORT: String(servicePort),
+ SERVER_ORIGIN: String(serviceOrigin),
+ PGHOST: String(pgConfig.host),
+ PGPORT: String(pgConfig.port),
+ PGDATABASE: String(pgConfig.database),
+ PGUSER: String(pgConfig.user),
+ PGPASSWORD: String(pgConfig.password),
+ NODE_OPTIONS: String(nodeOptions),
+ ...(graphileCacheMax != null ? { GRAPHILE_CACHE_MAX: String(graphileCacheMax) } : {}),
+ ...(graphileSchemaWaitTimeMs != null
+ ? { GRAPHILE_SCHEMA_WAIT_TIME_MS: String(graphileSchemaWaitTimeMs) }
+ : {}),
+ },
+ stdio: ['ignore', 'pipe', 'pipe'],
+ },
+ );
+
+ child.stdout.on('data', (chunk) => {
+ const text = String(chunk);
+ logStream.write(text);
+ process.stdout.write(text);
+ });
+
+ child.stderr.on('data', (chunk) => {
+ const text = String(chunk);
+ logStream.write(text);
+ process.stderr.write(text);
+ });
+
+ const readyUrl = `${baseUrl}/debug/memory`;
+
+ try {
+ await waitForServerReady({ url: readyUrl, timeoutMs: serviceReadyTimeoutMs });
+ await runRouteProbe({ profile: firstProfile });
+ return { child, logStream };
+ } catch (error) {
+ await stopServiceChild(child);
+ logStream.end();
+ throw new Error(`[k=${k}] failed to start usable service: ${error instanceof Error ? error.message : String(error)}`);
+ }
+};
+
+const main = async () => {
+ const dirs = await ensureRunDirs(runDir);
+ const kValues = parseKValues(kValuesRaw);
+ const profilesPayload = await readJson(profilesPath);
+ const profiles = extractProfiles(profilesPayload);
+ if (profiles.length === 0) {
+ throw new Error(`No profiles found in ${profilesPath}`);
+ }
+
+ const firstProfile = profiles[0];
+ const startedAt = new Date().toISOString();
+ const sweepResults = [];
+ const durationMinutes = Math.max(1, Math.round(durationSeconds / 60));
+ let abortError = null;
+
+ const buildTierLabel = (k) => {
+ if (routingMode === 'private' && tierMode === 'keyspace') {
+ return `business-k${k}-${durationMinutes}m-sweep`;
+ }
+ if (tierMode === 'active-tenants') {
+ return `business-${routingMode}-t${k}-${durationMinutes}m-sweep`;
+ }
+ return `business-${routingMode}-k${k}-${durationMinutes}m-sweep`;
+ };
+
+ for (const k of kValues) {
+ const tier = buildTierLabel(k);
+ const serviceLogPath = path.join(dirs.logsDir, 'service', `service-k${k}.log`);
+ const resetLogPath = path.join(dirs.logsDir, 'k-sweep', `reset-k${k}.log`);
+ const phase2LogPath = path.join(dirs.logsDir, 'k-sweep', `phase2-k${k}.log`);
+ const samplerDir = path.join(dirs.samplerDir, `k${k}`);
+ const phase2ProfileLimit = tierMode === 'active-tenants' ? Math.max(1, k) : 0;
+ const phase2KeyspaceSize = tierMode === 'keyspace' ? Math.max(1, k) : 1;
+ const phase2MinTenantCount = Math.max(
+ 1,
+ tierMode === 'active-tenants' ? Math.min(minTenantCount, phase2ProfileLimit) : minTenantCount,
+ );
+
+ const result = {
+ k,
+ tier,
+ routingMode,
+ tierMode,
+ apiIsPublic,
+ startedAt: new Date().toISOString(),
+ status: 'running',
+ serviceLogPath,
+ resetLogPath,
+ phase2LogPath,
+ samplerDir,
+ loadPath: path.join(dirs.dataDir, `load-${tier}.json`),
+ phase2ProfileLimit,
+ phase2KeyspaceSize,
+ phase2MinTenantCount,
+ error: null,
+ };
+ sweepResults.push(result);
+
+ let service = null;
+ try {
+ await stopPortListeners(servicePort);
+ service = await startServiceForK({ k, serviceLogPath, samplerDir, firstProfile });
+
+ const resetExecution = await runNodeScriptWithLog({
+ scriptPath: resetScript,
+ scriptArgs: [
+ '--run-dir',
+ runDir,
+ '--profiles',
+ profilesPath,
+ '--tag',
+ `k${k}`,
+ ...(ensurePublicTestAccess
+ ? [
+ '--ensure-public-test-access',
+ '--public-role',
+ publicRole,
+ ...(publicReadRole.length > 0 ? ['--public-read-role', publicReadRole] : []),
+ ]
+ : []),
+ ],
+ cwd: serverDir,
+ env: process.env,
+ logPath: resetLogPath,
+ });
+ if (resetExecution.code !== 0) {
+ throw new Error(`[k=${k}] reset script failed with code=${resetExecution.code}`);
+ }
+
+ const phase2Args = [
+ '--run-dir',
+ runDir,
+ '--profiles',
+ profilesPath,
+ '--workers',
+ String(workers),
+ '--duration-seconds',
+ String(durationSeconds),
+ '--idle-seconds',
+ String(idleSeconds),
+ '--min-tenant-count',
+ String(phase2MinTenantCount),
+ '--tier',
+ tier,
+ '--keyspace-mode',
+ keyspaceMode,
+ '--keyspace-size',
+ String(phase2KeyspaceSize),
+ '--churn-ratio',
+ String(churnRatio),
+ '--churn-warm-seconds',
+ String(churnWarmSeconds),
+ '--churn-cool-seconds',
+ String(churnCoolSeconds),
+ '--churn-cohorts',
+ String(churnCohorts),
+ '--prewarm-concurrency',
+ String(prewarmConcurrency),
+ '--prewarm-timeout-ms',
+ String(prewarmTimeoutMs),
+ '--prewarm-max-failures',
+ String(prewarmMaxFailures),
+ ];
+ if (prewarmDisabled) {
+ phase2Args.push('--disable-prewarm');
+ }
+ if (prewarmSampleSize > 0) {
+ phase2Args.push('--prewarm-sample-size', String(prewarmSampleSize));
+ }
+ if (phase2ProfileLimit > 0) {
+ phase2Args.push('--profile-limit', String(phase2ProfileLimit));
+ }
+
+ const phase2Execution = await runNodeScriptWithLog({
+ scriptPath: phase2Script,
+ scriptArgs: phase2Args,
+ cwd: serverDir,
+ env: process.env,
+ logPath: phase2LogPath,
+ abortOnExitProcess: service.child,
+ abortProcessLabel: `service[k=${k}]`,
+ });
+ if (phase2Execution.code !== 0) {
+ const guardHint = phase2Execution.abortedByGuard
+ ? `; aborted=${phase2Execution.abortedByGuard}`
+ : '';
+ throw new Error(`[k=${k}] phase2 failed with code=${phase2Execution.code}${guardHint}`);
+ }
+
+ await fsp.access(result.loadPath);
+ const loadPayload = await readJson(result.loadPath);
+ result.status = 'ok';
+ result.summary = {
+ total: loadPayload?.load?.total ?? 0,
+ failed: loadPayload?.load?.failed ?? 0,
+ requestsPerSecond: loadPayload?.load?.requestsPerSecond ?? 0,
+ p95: loadPayload?.load?.latencyMs?.p95 ?? null,
+ };
+ result.endedAt = new Date().toISOString();
+ } catch (error) {
+ result.status = 'failed';
+ result.error = error instanceof Error ? error.message : String(error);
+ result.endedAt = new Date().toISOString();
+ if (!continueOnError) {
+ abortError = error;
+ }
+ } finally {
+ if (service?.child) {
+ await stopServiceChild(service.child);
+ }
+ if (service?.logStream) {
+ service.logStream.end();
+ }
+ await stopPortListeners(servicePort);
+ }
+
+ if (abortError) {
+ break;
+ }
+ }
+
+ const summary = {
+ createdAt: new Date().toISOString(),
+ startedAt,
+ endedAt: new Date().toISOString(),
+ runDir,
+ profilesPath,
+ baseUrl,
+ options: {
+ apiIsPublic,
+ routingMode,
+ tierMode,
+ keyspaceMode,
+ ensurePublicTestAccess,
+ publicRole: ensurePublicTestAccess ? publicRole : null,
+ publicReadRole: ensurePublicTestAccess ? publicReadRole || null : null,
+ workers,
+ durationSeconds,
+ idleSeconds,
+ minTenantCount,
+ samplerMemoryIntervalMs,
+ samplerDbIntervalMs,
+ graphileCacheMax,
+ graphileSchemaWaitTimeMs,
+ prewarm: {
+ enabled: !prewarmDisabled,
+ sampleSize: prewarmSampleSize,
+ concurrency: prewarmConcurrency,
+ timeoutMs: prewarmTimeoutMs,
+ maxFailures: prewarmMaxFailures,
+ },
+ maxOldSpaceSizeMb,
+ nodeOptions,
+ kValues,
+ continueOnError,
+ },
+ results: sweepResults,
+ };
+
+ const summaryPath = path.join(dirs.reportsDir, 'k-sweep-summary.json');
+ await writeJson(summaryPath, summary);
+
+ console.log(
+ JSON.stringify(
+ {
+ summaryPath,
+ totalRuns: sweepResults.length,
+ failedRuns: sweepResults.filter((row) => row.status !== 'ok').length,
+ },
+ null,
+ 2,
+ ),
+ );
+
+ if (abortError) {
+ throw abortError;
+ }
+};
+
+main().catch(async (error) => {
+ try {
+ await stopPortListeners(servicePort);
+ } catch {
+ // ignore cleanup failure
+ }
+ console.error(error instanceof Error ? error.stack : String(error));
+ process.exit(1);
+});
diff --git a/graphql/server/perf/run-stress-suite.sh b/graphql/server/perf/run-stress-suite.sh
new file mode 100644
index 0000000000..22acf020a4
--- /dev/null
+++ b/graphql/server/perf/run-stress-suite.sh
@@ -0,0 +1,143 @@
+#!/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: postgres_perf database with test data
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SERVER_DIR="$(dirname "$SCRIPT_DIR")"
+cd "$SERVER_DIR"
+
+# Common env
+export PGHOST=127.0.0.1 PGPORT=5432 PGUSER=postgres PGPASSWORD=password PGDATABASE=postgres_perf
+export NODE_ENV=development GRAPHILE_ENV=development API_IS_PUBLIC=false GRAPHQL_OBSERVABILITY_ENABLED=true
+export SERVER_PORT=3000
+
+RESULTS_FILE="/tmp/stress-suite-results.jsonl"
+> "$RESULTS_FILE"
+
+kill_server() {
+ fuser -k $SERVER_PORT/tcp 2>/dev/null || true
+ sleep 2
+}
+
+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 curl -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 -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)
+ print(f' Heap: {m.get(\"heapUsed\",0)/1024/1024:.1f} MB, RSS: {m.get(\"rss\",0)/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/run-test-spec.mjs b/graphql/server/perf/run-test-spec.mjs
new file mode 100644
index 0000000000..af8a495500
--- /dev/null
+++ b/graphql/server/perf/run-test-spec.mjs
@@ -0,0 +1,61 @@
+#!/usr/bin/env node
+
+import path from 'node:path';
+import { spawn } from 'node:child_process';
+
+import {
+ DEFAULT_TMP_ROOT,
+ getArgValue,
+ makeRunId,
+} from './common.mjs';
+
+const args = process.argv.slice(2);
+const phase = getArgValue(args, '--phase', 'all');
+const runId = getArgValue(args, '--run-id', makeRunId());
+const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
+
+const forwardArgs = args.filter((arg, index) => {
+ const blocked = new Set(['--phase', '--run-id', '--run-dir']);
+ if (blocked.has(arg)) return false;
+ const prev = index > 0 ? args[index - 1] : '';
+ if (blocked.has(prev)) return false;
+ return true;
+});
+
+const runNodeScript = (scriptName, extraArgs = []) =>
+ new Promise((resolve, reject) => {
+ const scriptPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), scriptName);
+ const child = spawn(process.execPath, [scriptPath, '--run-dir', runDir, ...extraArgs, ...forwardArgs], {
+ stdio: 'inherit',
+ });
+
+ child.on('close', (code) => {
+ if (code === 0) {
+ resolve();
+ } else {
+ reject(new Error(`${scriptName} failed with exit code ${code}`));
+ }
+ });
+ });
+
+const main = async () => {
+ if (phase === 'phase1') {
+ await runNodeScript('phase1-preflight.mjs');
+ return;
+ }
+ if (phase === 'phase2') {
+ await runNodeScript('phase2-load.mjs');
+ return;
+ }
+ if (phase !== 'all') {
+ throw new Error(`Unknown --phase value: ${phase}`);
+ }
+
+ await runNodeScript('phase1-preflight.mjs');
+ await runNodeScript('phase2-load.mjs');
+};
+
+main().catch((error) => {
+ console.error(error instanceof Error ? error.stack : String(error));
+ process.exit(1);
+});
diff --git a/graphql/server/perf/seed-real-multitenant.mjs b/graphql/server/perf/seed-real-multitenant.mjs
new file mode 100644
index 0000000000..2607263c7b
--- /dev/null
+++ b/graphql/server/perf/seed-real-multitenant.mjs
@@ -0,0 +1,312 @@
+#!/usr/bin/env node
+
+import path from 'node:path';
+
+import {
+ DEFAULT_BASE_URL,
+ DEFAULT_TMP_ROOT,
+ ensureRunDirs,
+ getArgValue,
+ makeRunId,
+ postJson,
+ writeJson,
+} from './common.mjs';
+
+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 tenantCount = Number.parseInt(getArgValue(args, '--tenant-count', '10'), 10);
+const userPassword = getArgValue(args, '--user-password', 'Constructive!23456');
+const userPrefix = getArgValue(args, '--user-prefix', `mtlt-${Date.now()}`);
+const orgPrefix = getArgValue(args, '--org-prefix', `mtlt-org-${Date.now()}`);
+
+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 adminEmail = getArgValue(args, '--admin-email', 'admin@constructive.io');
+const adminPassword = getArgValue(args, '--admin-password', 'admin123!@Constructive');
+
+const routeHeaders = {
+ Host: routeHost,
+ 'X-Api-Name': privateApiName,
+ 'X-Database-Id': privateDatabaseId,
+};
+
+const gql = async ({ query, variables, headers = {} }) => {
+ return await postJson({
+ url: `${baseUrl}/graphql`,
+ headers: {
+ ...routeHeaders,
+ ...headers,
+ },
+ payload: { query, variables },
+ timeoutMs: 20000,
+ });
+};
+
+const signInMutation = `
+mutation SignIn($input: SignInInput!) {
+ signIn(input: $input) {
+ result {
+ id
+ userId
+ accessToken
+ }
+ }
+}
+`;
+
+const signUpMutation = `
+mutation SignUp($input: SignUpInput!) {
+ signUp(input: $input) {
+ result {
+ id
+ userId
+ accessToken
+ }
+ }
+}
+`;
+
+const createUserMutation = `
+mutation CreateUser($input: CreateUserInput!) {
+ createUser(input: $input) {
+ user {
+ id
+ type
+ displayName
+ }
+ }
+}
+`;
+
+const createOrgMembershipMutation = `
+mutation CreateOrgMembership($input: CreateOrgMembershipInput!) {
+ createOrgMembership(input: $input) {
+ orgMembership {
+ id
+ actorId
+ entityId
+ isOwner
+ isAdmin
+ isActive
+ isApproved
+ }
+ }
+}
+`;
+
+const signInAdmin = async () => {
+ const res = await gql({
+ query: signInMutation,
+ variables: {
+ input: {
+ email: adminEmail,
+ password: adminPassword,
+ rememberMe: true,
+ },
+ },
+ });
+
+ const token = res.json?.data?.signIn?.result?.accessToken;
+ const userId = res.json?.data?.signIn?.result?.userId;
+ if (!res.ok || !token || !userId) {
+ throw new Error(
+ `Admin sign-in failed for ${adminEmail}; status=${res.status}; error=${
+ res.error || res.json?.errors?.[0]?.message || 'no token returned'
+ }`,
+ );
+ }
+
+ return { token, userId };
+};
+
+const signUpOrSignInUser = async ({ email, password }) => {
+ const signUpRes = await gql({
+ query: signUpMutation,
+ 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,
+ 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(
+ `User auth failed for ${email}; signUpError=${
+ signUpRes.error || signUpRes.json?.errors?.[0]?.message || 'null result'
+ }; signInError=${signInRes.error || signInRes.json?.errors?.[0]?.message || 'null result'}`,
+ );
+};
+
+const createOrg = async ({ adminToken, displayName }) => {
+ const res = await gql({
+ query: createUserMutation,
+ headers: { Authorization: `Bearer ${adminToken}` },
+ variables: {
+ input: {
+ user: {
+ displayName,
+ type: 2,
+ },
+ },
+ },
+ });
+
+ const user = res.json?.data?.createUser?.user;
+ if (!res.ok || !user?.id) {
+ throw new Error(
+ `Create org failed (${displayName}); status=${res.status}; error=${
+ res.error || res.json?.errors?.[0]?.message || 'null result'
+ }`,
+ );
+ }
+
+ return user;
+};
+
+const createMembership = async ({ adminToken, actorId, entityId }) => {
+ const res = await gql({
+ query: createOrgMembershipMutation,
+ headers: { Authorization: `Bearer ${adminToken}` },
+ variables: {
+ input: {
+ orgMembership: {
+ actorId,
+ entityId,
+ isOwner: true,
+ isAdmin: true,
+ isActive: true,
+ isApproved: true,
+ },
+ },
+ },
+ });
+
+ const membership = res.json?.data?.createOrgMembership?.orgMembership;
+ if (!res.ok || !membership?.id) {
+ throw new Error(
+ `Create org membership failed actor=${actorId} entity=${entityId}; status=${res.status}; error=${
+ res.error || res.json?.errors?.[0]?.message || 'null result'
+ }`,
+ );
+ }
+
+ return membership;
+};
+
+const main = async () => {
+ if (!Number.isFinite(tenantCount) || tenantCount < 1) {
+ throw new Error(`Invalid --tenant-count: ${tenantCount}`);
+ }
+
+ const dirs = await ensureRunDirs(runDir);
+ const admin = await signInAdmin();
+
+ const manifest = [];
+ const credentials = [];
+
+ for (let i = 1; i <= tenantCount; i += 1) {
+ const idx = String(i).padStart(2, '0');
+ const orgDisplayName = `${orgPrefix}-${idx}`;
+ const email = `${userPrefix}-${idx}@example.com`;
+
+ const org = await createOrg({ adminToken: admin.token, displayName: orgDisplayName });
+ const user = await signUpOrSignInUser({ email, password: userPassword });
+ const membership = await createMembership({
+ adminToken: admin.token,
+ actorId: user.userId,
+ entityId: org.id,
+ });
+
+ manifest.push({
+ tenantKey: `org:${org.id}`,
+ orgId: org.id,
+ orgDisplayName,
+ userId: user.userId,
+ userEmail: email,
+ userAuthMode: user.mode,
+ membershipId: membership.id,
+ });
+
+ credentials.push({
+ tenantKey: `org:${org.id}`,
+ email,
+ password: userPassword,
+ host: routeHost,
+ apiName: privateApiName,
+ databaseId: privateDatabaseId,
+ });
+ }
+
+ const manifestPath = path.join(dirs.dataDir, 'tenant-manifest.json');
+ const credentialsPath = path.join(dirs.dataDir, 'tenant-credentials.json');
+ const summaryPath = path.join(dirs.reportsDir, 'seed-summary.json');
+
+ await writeJson(manifestPath, manifest);
+ await writeJson(credentialsPath, credentials);
+ await writeJson(summaryPath, {
+ createdAt: new Date().toISOString(),
+ runDir,
+ tenantCount,
+ route: routeHeaders,
+ adminUserId: admin.userId,
+ manifestPath,
+ credentialsPath,
+ });
+
+ console.log(
+ JSON.stringify(
+ {
+ runDir,
+ tenantCount,
+ manifestPath,
+ credentialsPath,
+ summaryPath,
+ },
+ null,
+ 2,
+ ),
+ );
+};
+
+main().catch((error) => {
+ console.error(error instanceof Error ? error.stack : String(error));
+ process.exit(1);
+});
diff --git a/graphql/server/perf/summarize-shapes.mjs b/graphql/server/perf/summarize-shapes.mjs
new file mode 100644
index 0000000000..0b521e0ec9
--- /dev/null
+++ b/graphql/server/perf/summarize-shapes.mjs
@@ -0,0 +1,222 @@
+#!/usr/bin/env node
+
+/**
+ * summarize-shapes.mjs — Perf-local structural shape diagnostic.
+ *
+ * Reads the business-table-manifest.json produced by phase1-tech-validate-dbpm
+ * and queries information_schema.columns for each tenant's physical schema to
+ * build a **name-stripped structural fingerprint**.
+ *
+ * The fingerprint ignores:
+ * - schema names
+ * - table names
+ * - column names
+ *
+ * It retains:
+ * - per-table column count
+ * - per-column data_type and is_nullable (ordered by ordinal_position)
+ * - number of tables per schema
+ *
+ * Tenants are grouped by fingerprint so that structurally identical schemas
+ * (even with different tenant-specific names) collapse into one group, while
+ * genuinely different shapes (e.g. extra variant tables) form separate groups.
+ *
+ * Usage:
+ * node summarize-shapes.mjs --manifest [pg options]
+ *
+ * Output:
+ * - number of distinct structural groups
+ * - tenant count per group
+ * - column layout summary per group
+ */
+
+import { createHash } from 'node:crypto';
+import { readFileSync } from 'node:fs';
+import { Pool } from 'pg';
+
+import { getArgValue } from './common.mjs';
+
+const args = process.argv.slice(2);
+
+const manifestPath = getArgValue(args, '--manifest', null);
+if (!manifestPath) {
+ console.error('Usage: node summarize-shapes.mjs --manifest ');
+ process.exit(1);
+}
+
+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'),
+};
+
+// ---------------------------------------------------------------------------
+// Structural fingerprinting
+// ---------------------------------------------------------------------------
+
+/**
+ * Build a name-stripped structural signature for one table.
+ *
+ * Returns an array of [data_type, is_nullable] tuples ordered by
+ * ordinal_position. The table name and column names are NOT included.
+ */
+const buildTableSignature = (columns) =>
+ columns
+ .sort((a, b) => a.ordinal_position - b.ordinal_position)
+ .map((c) => `${c.data_type}:${c.is_nullable}`);
+
+/**
+ * Build a name-stripped structural fingerprint for an entire schema.
+ *
+ * 1. Group columns by table
+ * 2. Build per-table signature (column types + nullability, ordered)
+ * 3. Sort tables by their signature (lexicographic on the stringified tuple list)
+ * 4. Hash the sorted result → structural fingerprint
+ *
+ * Two schemas with identical column layouts but different table/column names
+ * will produce the same fingerprint.
+ */
+const buildSchemaFingerprint = (rows) => {
+ // Group by table_name
+ const tables = new Map();
+ for (const row of rows) {
+ if (!tables.has(row.table_name)) {
+ tables.set(row.table_name, []);
+ }
+ tables.get(row.table_name).push(row);
+ }
+
+ // Build per-table signatures and sort tables by signature
+ const tableSignatures = [];
+ for (const [, columns] of tables) {
+ tableSignatures.push(buildTableSignature(columns));
+ }
+
+ // Sort tables by their stringified signature so table order is deterministic
+ tableSignatures.sort((a, b) => {
+ const sa = JSON.stringify(a);
+ const sb = JSON.stringify(b);
+ return sa < sb ? -1 : sa > sb ? 1 : 0;
+ });
+
+ const canonical = JSON.stringify(tableSignatures);
+ const fingerprint = createHash('sha256').update(canonical).digest('hex').slice(0, 16);
+
+ return {
+ fingerprint,
+ tableCount: tableSignatures.length,
+ tableSummaries: tableSignatures.map((sig) => ({
+ columnCount: sig.length,
+ columns: sig,
+ })),
+ };
+};
+
+// ---------------------------------------------------------------------------
+// Main
+// ---------------------------------------------------------------------------
+
+const main = async () => {
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
+ if (!Array.isArray(manifest) || manifest.length === 0) {
+ console.error('Manifest is empty or not an array');
+ process.exit(1);
+ }
+
+ const pool = new Pool(pgConfig);
+
+ try {
+ /** @type {Map} */
+ const groups = new Map();
+
+ for (const entry of manifest) {
+ const schema = entry.physicalSchema;
+ if (!schema) {
+ console.warn(`Skipping tenant ${entry.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 ${entry.tenantKey}: no columns found in schema ${schema}`);
+ continue;
+ }
+
+ const { fingerprint, tableCount, tableSummaries } = buildSchemaFingerprint(result.rows);
+
+ if (!groups.has(fingerprint)) {
+ groups.set(fingerprint, {
+ tenants: [],
+ tableCount,
+ tableSummaries,
+ });
+ }
+ groups.get(fingerprint).tenants.push(entry.tenantKey);
+ }
+
+ // ---------------------------------------------------------------------------
+ // Output
+ // ---------------------------------------------------------------------------
+
+ 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 [fp, group] of groups) {
+ groupIndex += 1;
+ console.log(`--- Group ${groupIndex} (fingerprint: ${fp}) ---`);
+ console.log(` Tenants: ${group.tenants.length}`);
+ console.log(` Tables: ${group.tableCount}`);
+ for (let t = 0; t < group.tableSummaries.length; t++) {
+ const ts = group.tableSummaries[t];
+ console.log(` Table ${t + 1}: ${ts.columnCount} columns`);
+ for (const col of ts.columns) {
+ console.log(` - ${col}`);
+ }
+ }
+ 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('');
+ }
+
+ // Machine-readable summary to stdout
+ const summary = {
+ totalTenants: manifest.length,
+ distinctGroups: groups.size,
+ groups: [...groups.entries()].map(([fp, g]) => ({
+ fingerprint: fp,
+ tenantCount: g.tenants.length,
+ tableCount: g.tableCount,
+ columnLayouts: g.tableSummaries.map((ts) => ({
+ columnCount: ts.columnCount,
+ columns: ts.columns,
+ })),
+ })),
+ };
+
+ console.log('--- JSON Summary ---');
+ console.log(JSON.stringify(summary, null, 2));
+ } finally {
+ await pool.end();
+ }
+};
+
+main().catch((error) => {
+ console.error(error instanceof Error ? error.stack : String(error));
+ process.exit(1);
+});
diff --git a/graphql/server/src/index.ts b/graphql/server/src/index.ts
index 06d1561bd8..03daf86274 100644
--- a/graphql/server/src/index.ts
+++ b/graphql/server/src/index.ts
@@ -5,5 +5,5 @@ export { createApiMiddleware, getSubdomain, getApiConfig } from './middleware/ap
export { createAuthenticateMiddleware } from './middleware/auth';
export { createUploadAuthenticateMiddleware } from './middleware/upload';
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/flush.ts b/graphql/server/src/middleware/flush.ts
index 653d74631c..d5716ce104 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 (
});
}
+ // v4-buildkey: 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 354247f334..93aa45ee93 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 { ConstructivePreset, makePgService } from 'graphile-settings';
import { getPgPool } from 'pg-cache';
import { getPgEnvOptions } from 'pg-env';
+import {
+ configureMultiTenancyCache,
+ getTenantInstance,
+ getOrCreateTenantInstance,
+ shutdownMultiTenancyCache,
+ flushByDatabaseId,
+} from 'graphile-multi-tenancy-cache';
import './types'; // for Request type
import { isGraphqlObservabilityEnabled } from '../diagnostics/observability';
import { HandlerCreationError } from '../errors/api-errors';
@@ -310,3 +317,93 @@ 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() — no preset builder passed from server.
+ * Routes request to tenant.handler — the package's Grafast context callback
+ * handles pgSqlTextTransform injection internally.
+ */
+export const multiTenancyHandler = (opts: ConstructiveOptions): RequestHandler => {
+ // One-time bootstrap: configure the multi-tenancy cache with our preset builder
+ configureMultiTenancyCache({
+ basePresetBuilder: buildPreset,
+ });
+
+ 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,
+ });
+
+ 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);
+ }
+ };
+};
diff --git a/graphql/server/src/server.ts b/graphql/server/src/server.ts
index 7aa57c4b24..74f025cac7 100644
--- a/graphql/server/src/server.ts
+++ b/graphql/server/src/server.ts
@@ -25,8 +25,8 @@ 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 { flush, createFlushMiddleware, flushService } from './middleware/flush';
+import { graphile, multiTenancyHandler, isMultiTenancyCacheEnabled, shutdownMultiTenancy } from './middleware/graphile';
import { multipartBridge } from './middleware/multipart-bridge';
import { createDebugDatabaseMiddleware } from './middleware/observability/debug-db';
import { debugMemory } from './middleware/observability/debug-memory';
@@ -101,6 +101,7 @@ class Server {
exposedSchemas: apiOpts.exposedSchemas?.join(',') || 'none',
anonRole: apiOpts.anonRole,
roleName: apiOpts.roleName,
+ useMultiTenancyCache: apiOpts.useMultiTenancyCache,
observabilityEnabled,
});
@@ -158,8 +159,16 @@ class Server {
app.use(api);
app.post('/upload', uploadAuthenticate, ...uploadRoute);
app.use(authenticate);
- app.use(graphile(effectiveOpts));
- app.use(flush);
+
+ // Select handler based on multi-tenancy cache mode
+ if (isMultiTenancyCacheEnabled(effectiveOpts)) {
+ log.info('[server] Multi-tenancy cache ENABLED');
+ app.use(multiTenancyHandler(effectiveOpts));
+ app.use(createFlushMiddleware(effectiveOpts));
+ } else {
+ app.use(graphile(effectiveOpts));
+ app.use(flush);
+ }
// Error handling - MUST be LAST
app.use(notFoundHandler); // Catches unmatched routes (404)
@@ -290,6 +299,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..4cf4548edc 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 (template-based instance sharing across tenants) */
+ useMultiTenancyCache?: boolean;
}
/**
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a2949e9224..0173822778 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,6 +12,7 @@ overrides:
packageExtensionsChecksum: sha256-x8B4zkJ4KLRX+yspUWxuggXWlz6zrBLSIh72pNhpPiE=
importers:
+
.:
devDependencies:
'@jest/test-sequencer':
@@ -215,7 +216,7 @@ importers:
version: 5.2.1
grafserv:
specifier: 1.0.0
- version: 1.0.0(@types/node@22.19.15)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
+ version: 1.0.0(@types/node@25.5.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
lru-cache:
specifier: ^11.2.7
version: 11.2.7
@@ -224,7 +225,7 @@ importers:
version: link:../../postgres/pg-cache/dist
postgraphile:
specifier: 5.0.0
- version: 5.0.0(0c2cedda320a650bce7ee949e4b7e993)
+ version: 5.0.0(f35d86129e8192df0ebe9574df8f7655)
devDependencies:
'@types/express':
specifier: ^5.0.6
@@ -237,7 +238,7 @@ importers:
version: 3.1.14
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
graphile/graphile-connection-filter:
@@ -278,6 +279,53 @@ 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
+ grafserv:
+ specifier: 1.0.0
+ version: 1.0.0(@types/node@25.5.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
+ graphile-config:
+ specifier: 1.0.0
+ version: 1.0.0
+ pg:
+ specifier: ^8.11.3
+ version: 8.20.0
+ pg-env:
+ specifier: workspace:^
+ version: link:../../postgres/pg-env/dist
+ pg-introspection:
+ specifier: 1.0.0
+ version: 1.0.0
+ pgsql-deparser:
+ specifier: ^17.18.2
+ version: 17.18.2
+ pgsql-parser:
+ specifier: ^17.9.14
+ version: 17.9.14
+ postgraphile:
+ specifier: 5.0.0
+ version: 5.0.0(f35d86129e8192df0ebe9574df8f7655)
+ devDependencies:
+ '@types/express':
+ specifier: ^5.0.6
+ version: 5.0.6
+ '@types/pg':
+ specifier: ^8.10.9
+ version: 8.18.0
+ makage:
+ specifier: ^0.3.0
+ version: 0.3.0
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
+ publishDirectory: dist
+
graphile/graphile-postgis:
dependencies:
'@dataplan/pg':
@@ -439,7 +487,7 @@ importers:
version: 0.3.0
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
graphile/graphile-search:
@@ -535,7 +583,7 @@ importers:
version: 1.0.0(graphql@16.13.0)
grafserv:
specifier: 1.0.0
- version: 1.0.0(@types/node@22.19.15)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
+ version: 1.0.0(@types/node@25.5.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
graphile-bucket-provisioner-plugin:
specifier: workspace:*
version: link:../graphile-bucket-provisioner-plugin/dist
@@ -589,7 +637,7 @@ importers:
version: 5.0.0
postgraphile:
specifier: 5.0.0
- version: 5.0.0(0c2cedda320a650bce7ee949e4b7e993)
+ version: 5.0.0(f35d86129e8192df0ebe9574df8f7655)
request-ip:
specifier: ^3.3.0
version: 3.3.0
@@ -623,7 +671,7 @@ importers:
version: link:../../postgres/pgsql-test/dist
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
graphile/graphile-sql-expression-validator:
@@ -871,7 +919,7 @@ importers:
version: 5.2.1
grafserv:
specifier: 1.0.0
- version: 1.0.0(@types/node@22.19.15)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
+ version: 1.0.0(@types/node@25.5.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
graphile-cache:
specifier: workspace:^
version: link:../../graphile/graphile-cache/dist
@@ -892,7 +940,7 @@ importers:
version: link:../../postgres/pg-env/dist
postgraphile:
specifier: 5.0.0
- version: 5.0.0(0c2cedda320a650bce7ee949e4b7e993)
+ version: 5.0.0(f35d86129e8192df0ebe9574df8f7655)
devDependencies:
'@types/express':
specifier: ^5.0.6
@@ -905,7 +953,7 @@ importers:
version: 3.1.14
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
graphql/gql-ast:
@@ -979,7 +1027,7 @@ importers:
version: link:../../postgres/pgsql-test/dist
ts-jest:
specifier: ^29.1.0
- version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.19.11)(ts-node@10.9.2(@types/node@22.19.11)(typescript@5.9.3)))(typescript@5.9.3)
+ version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3)))(typescript@5.9.3)
typescript:
specifier: ^5.0.0
version: 5.9.3
@@ -1162,7 +1210,7 @@ importers:
version: 1.0.0(graphql@16.13.0)
grafserv:
specifier: 1.0.0
- version: 1.0.0(@types/node@22.19.15)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
+ version: 1.0.0(@types/node@25.5.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
graphile-build:
specifier: 5.0.0
version: 5.0.0(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)
@@ -1175,6 +1223,9 @@ importers:
graphile-config:
specifier: 1.0.0
version: 1.0.0
+ graphile-multi-tenancy-cache:
+ specifier: workspace:^
+ version: link:../../graphile/graphile-multi-tenancy-cache/dist
graphile-settings:
specifier: workspace:^
version: link:../../graphile/graphile-settings/dist
@@ -1210,7 +1261,7 @@ importers:
version: 5.0.0
postgraphile:
specifier: 5.0.0
- version: 5.0.0(0c2cedda320a650bce7ee949e4b7e993)
+ version: 5.0.0(f35d86129e8192df0ebe9574df8f7655)
request-ip:
specifier: ^3.3.0
version: 3.3.0
@@ -1247,7 +1298,7 @@ importers:
version: 3.1.14
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
graphql/server-test:
@@ -1660,7 +1711,7 @@ importers:
version: 7.2.2
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
jobs/knative-job-worker:
@@ -1962,7 +2013,7 @@ importers:
version: 0.3.0
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
packages/smtppostmaster:
@@ -1991,7 +2042,7 @@ importers:
version: 3.18.1
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3)
+ version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
publishDirectory: dist
packages/upload-client:
@@ -2854,11 +2905,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:
@@ -2866,293 +2915,167 @@ packages:
optional: true
'@agentic-kit/ollama@1.0.3':
- resolution:
- {
- integrity: sha512-bvDMjofjgSTI8oLvFI/8ORQvO+wrwmdYETYCB1yAOSy622zB6DWYqk+ldK7mKjZ22iWrjkoWFEdSY7T9rYE1FA==,
- }
+ resolution: {integrity: sha512-bvDMjofjgSTI8oLvFI/8ORQvO+wrwmdYETYCB1yAOSy622zB6DWYqk+ldK7mKjZ22iWrjkoWFEdSY7T9rYE1FA==}
'@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.1009.0':
- resolution:
- {
- integrity: sha512-luy8CxallkoiGWTqU86ca/BbvkWJjs0oala7uIIRN1JtQxMb5i4Yl/PBZVcQFhbK9kQi0PK0GfD8gIpLkI91fw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-luy8CxallkoiGWTqU86ca/BbvkWJjs0oala7uIIRN1JtQxMb5i4Yl/PBZVcQFhbK9kQi0PK0GfD8gIpLkI91fw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/core@3.973.24':
- resolution:
- {
- integrity: sha512-vvf82RYQu2GidWAuQq+uIzaPz9V0gSCXVqdVzRosgl5rXcspXOpSD3wFreGGW6AYymPr97Z69kjVnLePBxloDw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-vvf82RYQu2GidWAuQq+uIzaPz9V0gSCXVqdVzRosgl5rXcspXOpSD3wFreGGW6AYymPr97Z69kjVnLePBxloDw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/crc64-nvme@3.972.5':
- resolution:
- {
- integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-env@3.972.18':
- resolution:
- {
- integrity: sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-http@3.972.20':
- resolution:
- {
- integrity: sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-ini@3.972.20':
- resolution:
- {
- integrity: sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-login@3.972.20':
- resolution:
- {
- integrity: sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-node@3.972.21':
- resolution:
- {
- integrity: sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-process@3.972.18':
- resolution:
- {
- integrity: sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-sso@3.972.20':
- resolution:
- {
- integrity: sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-web-identity@3.972.20':
- resolution:
- {
- integrity: sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/lib-storage@3.1009.0':
- resolution:
- {
- integrity: sha512-gHQh1sNeTuxZxPSMSQWOq/Xli8I5499uWyRKMakMSv8N7IYfoyDdyT52Ul6697qcqVaoPHixmYTllfEWMo1AKg==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-gHQh1sNeTuxZxPSMSQWOq/Xli8I5499uWyRKMakMSv8N7IYfoyDdyT52Ul6697qcqVaoPHixmYTllfEWMo1AKg==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
'@aws-sdk/client-s3': ^3.1009.0
'@aws-sdk/middleware-bucket-endpoint@3.972.8':
- resolution:
- {
- integrity: sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-expect-continue@3.972.8':
- resolution:
- {
- integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-flexible-checksums@3.973.6':
- resolution:
- {
- integrity: sha512-0nYEgkJH7Yt9k+nZJyllTghnkKaz17TWFcr5Mi0XMVMzYlF4ytDZADQpF2/iJo36cKL5AYSzRsvlykE4M/ErTA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-0nYEgkJH7Yt9k+nZJyllTghnkKaz17TWFcr5Mi0XMVMzYlF4ytDZADQpF2/iJo36cKL5AYSzRsvlykE4M/ErTA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-host-header@3.972.8':
- resolution:
- {
- integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-location-constraint@3.972.8':
- resolution:
- {
- integrity: sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-logger@3.972.8':
- resolution:
- {
- integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-recursion-detection@3.972.8':
- resolution:
- {
- integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-sdk-s3@3.972.25':
- resolution:
- {
- integrity: sha512-4xJL7O+XkhbSkT4yAYshkAww+mxJvtGQneNHH0MOpe+w8Vo2z87M9z06UO3G6zPM2c3Ef2yKczvZpTgdArMHfg==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-4xJL7O+XkhbSkT4yAYshkAww+mxJvtGQneNHH0MOpe+w8Vo2z87M9z06UO3G6zPM2c3Ef2yKczvZpTgdArMHfg==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-ssec@3.972.8':
- resolution:
- {
- integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-user-agent@3.972.21':
- resolution:
- {
- integrity: sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/nested-clients@3.996.10':
- resolution:
- {
- integrity: sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/region-config-resolver@3.972.8':
- resolution:
- {
- integrity: sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/s3-request-presigner@3.1017.0':
- resolution:
- {
- integrity: sha512-PSR8VJEkCy53uhAeuvht6ub3kzfdqoTAmLliQJ63MkC/1FuMmrmqWRGoZuzZvAbpzTcZtuibSGvawDa47gsckA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-PSR8VJEkCy53uhAeuvht6ub3kzfdqoTAmLliQJ63MkC/1FuMmrmqWRGoZuzZvAbpzTcZtuibSGvawDa47gsckA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/signature-v4-multi-region@3.996.13':
- resolution:
- {
- integrity: sha512-7j8rOFHHq4e9McCSuWBmBSADriW5CjPUem4inckRh/cyQGaijBwDbkNbVTgDVDWqFo29SoVVUfI6HCOnck6HZw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-7j8rOFHHq4e9McCSuWBmBSADriW5CjPUem4inckRh/cyQGaijBwDbkNbVTgDVDWqFo29SoVVUfI6HCOnck6HZw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/token-providers@3.1009.0':
- resolution:
- {
- integrity: sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/types@3.973.6':
- resolution:
- {
- integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/util-arn-parser@3.972.3':
- resolution:
- {
- integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/util-endpoints@3.996.5':
- resolution:
- {
- integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/util-format-url@3.972.8':
- resolution:
- {
- integrity: sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A==}
+ 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/util-user-agent-browser@3.972.8':
- resolution:
- {
- integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==,
- }
+ resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==}
'@aws-sdk/util-user-agent-node@3.973.7':
- resolution:
- {
- integrity: sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
aws-crt: '>=1.0.0'
peerDependenciesMeta:
@@ -3160,423 +3083,258 @@ packages:
optional: true
'@aws-sdk/xml-builder@3.972.15':
- resolution:
- {
- integrity: sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA==,
- }
- engines: { node: '>=20.0.0' }
+ resolution: {integrity: sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA==}
+ 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' }
+ 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-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.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.27.1':
- resolution:
- {
- integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==,
- }
- engines: { node: '>=6.9.0' }
+ resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+ 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.27.1':
- resolution:
- {
- integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==,
- }
- engines: { node: '>=6.9.0' }
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.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/parser@7.28.6':
- resolution:
- {
- integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==,
- }
- engines: { node: '>=6.0.0' }
+ resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==}
+ engines: {node: '>=6.0.0'}
hasBin: true
'@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/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.27.1':
- resolution:
- {
- integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==,
- }
- engines: { node: '>=6.9.0' }
+ resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+ engines: {node: '>=6.9.0'}
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==}
'@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'}
'@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.0':
- resolution:
- {
- integrity: sha512-Nl4cdQWgdl86u78K1FjQtvH+AyH5ToDb9hYxN99Hu8T+ip6a6B3i3Ho0nRlBccUWYHx+p92Kh70sDXCJ3Fpmnw==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-Nl4cdQWgdl86u78K1FjQtvH+AyH5ToDb9hYxN99Hu8T+ip6a6B3i3Ho0nRlBccUWYHx+p92Kh70sDXCJ3Fpmnw==}
+ engines: {node: '>=22'}
peerDependencies:
'@dataplan/json': 1.0.0
grafast: ^1.0.0
@@ -3589,668 +3347,425 @@ packages:
optional: true
'@emnapi/core@1.7.1':
- resolution:
- {
- integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==,
- }
+ resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==}
'@emnapi/core@1.9.0':
- resolution:
- {
- integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==,
- }
+ resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==}
'@emnapi/runtime@1.7.1':
- resolution:
- {
- integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==,
- }
+ resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==}
'@emnapi/runtime@1.9.0':
- resolution:
- {
- integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==,
- }
+ resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==}
'@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.0':
- resolution:
- {
- integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==,
- }
+ resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==}
'@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'}
'@graphile/simplify-inflection@8.0.0':
- resolution:
- {
- integrity: sha512-fzC4zz456Km8oZQMiBU03kgSRQxT+aS3sEdEQIcvkfdUhJKlIk81ll8UaxLLkl/WRb7TByNM1VfrY9mEa4w9kw==,
- }
+ resolution: {integrity: sha512-fzC4zz456Km8oZQMiBU03kgSRQxT+aS3sEdEQIcvkfdUhJKlIk81ll8UaxLLkl/WRb7TByNM1VfrY9mEa4w9kw==}
'@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
@@ -4259,10 +3774,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
@@ -4270,10 +3782,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
@@ -4281,10 +3790,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'
@@ -4293,64 +3799,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:
@@ -4358,10 +3840,7 @@ packages:
optional: true
'@inquirerer/test@1.3.5':
- resolution:
- {
- integrity: sha512-oA8rqZTDAqgf3GJ48KFX6/cLrnm9D8qzqseU1mJuFibDheb2TpGzR7cshCwQtnc4zuOEAl5QyLrg8BHhX5DR4Q==,
- }
+ resolution: {integrity: sha512-oA8rqZTDAqgf3GJ48KFX6/cLrnm9D8qzqseU1mJuFibDheb2TpGzR7cshCwQtnc4zuOEAl5QyLrg8BHhX5DR4Q==}
peerDependencies:
jest: '>=29.0.0'
peerDependenciesMeta:
@@ -4369,58 +3848,34 @@ packages:
optional: true
'@inquirerer/utils@3.3.5':
- resolution:
- {
- integrity: sha512-ENzkQImZ59Y2wegY4ng9MsCLe2CZjWGO6WXFpoppGQmZqgIOAzsmGHpdQxORRS+I4RnzA/7Tr+ZUJ6bSpC7JWg==,
- }
+ resolution: {integrity: sha512-ENzkQImZ59Y2wegY4ng9MsCLe2CZjWGO6WXFpoppGQmZqgIOAzsmGHpdQxORRS+I4RnzA/7Tr+ZUJ6bSpC7JWg==}
'@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.3':
- resolution:
- {
- integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==,
- }
- engines: { node: '>=8' }
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
'@jest/console@30.3.0':
- resolution:
- {
- integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/core@30.3.0':
- resolution:
- {
- integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==}
+ 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:
@@ -4428,81 +3883,48 @@ 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.3.0':
- resolution:
- {
- integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/environment@30.3.0':
- resolution:
- {
- integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==}
+ 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.3.0':
- resolution:
- {
- integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/expect@30.3.0':
- resolution:
- {
- integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/fake-timers@30.3.0':
- resolution:
- {
- integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==}
+ 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.3.0':
- resolution:
- {
- integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==}
+ 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/reporters@30.3.0':
- resolution:
- {
- integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==}
+ 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:
@@ -4510,820 +3932,499 @@ 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/snapshot-utils@30.3.0':
- resolution:
- {
- integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==}
+ 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.3.0':
- resolution:
- {
- integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/test-sequencer@30.3.0':
- resolution:
- {
- integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/transform@30.3.0':
- resolution:
- {
- integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==}
+ 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.3.0':
- resolution:
- {
- integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==}
+ 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.12':
- resolution:
- {
- integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==,
- }
+ resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
'@napi-rs/wasm-runtime@0.2.4':
- resolution:
- {
- integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==,
- }
+ resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==}
'@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}
'@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.42.0':
- resolution:
- {
- integrity: sha512-dsqPTYsozeokRjlrt/b4E7Pj0z3eS3Eg74TWQuuKbjY4VttBmA88rB7d50Xrd+TZ986qdXCNeZRPEzZHAe+jow==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-dsqPTYsozeokRjlrt/b4E7Pj0z3eS3Eg74TWQuuKbjY4VttBmA88rB7d50Xrd+TZ986qdXCNeZRPEzZHAe+jow==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
'@oxfmt/binding-android-arm64@0.42.0':
- resolution:
- {
- integrity: sha512-t+aAjHxcr5eOBphFHdg1ouQU9qmZZoRxnX7UOJSaTwSoKsb6TYezNKO0YbWytGXCECObRqNcUxPoPr0KaraAIg==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-t+aAjHxcr5eOBphFHdg1ouQU9qmZZoRxnX7UOJSaTwSoKsb6TYezNKO0YbWytGXCECObRqNcUxPoPr0KaraAIg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@oxfmt/binding-darwin-arm64@0.42.0':
- resolution:
- {
- integrity: sha512-ulpSEYMKg61C5bRMZinFHrKJYRoKGVbvMEXA5zM1puX3O9T6Q4XXDbft20yrDijpYWeuG59z3Nabt+npeTsM1A==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-ulpSEYMKg61C5bRMZinFHrKJYRoKGVbvMEXA5zM1puX3O9T6Q4XXDbft20yrDijpYWeuG59z3Nabt+npeTsM1A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@oxfmt/binding-darwin-x64@0.42.0':
- resolution:
- {
- integrity: sha512-ttxLKhQYPdFiM8I/Ri37cvqChE4Xa562nNOsZFcv1CKTVLeEozXjKuYClNvxkXmNlcF55nzM80P+CQkdFBu+uQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-ttxLKhQYPdFiM8I/Ri37cvqChE4Xa562nNOsZFcv1CKTVLeEozXjKuYClNvxkXmNlcF55nzM80P+CQkdFBu+uQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@oxfmt/binding-freebsd-x64@0.42.0':
- resolution:
- {
- integrity: sha512-Og7QS3yI3tdIKYZ58SXik0rADxIk2jmd+/YvuHRyKULWpG4V2fR5V4hvKm624Mc0cQET35waPXiCQWvjQEjwYQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-Og7QS3yI3tdIKYZ58SXik0rADxIk2jmd+/YvuHRyKULWpG4V2fR5V4hvKm624Mc0cQET35waPXiCQWvjQEjwYQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@oxfmt/binding-linux-arm-gnueabihf@0.42.0':
- resolution:
- {
- integrity: sha512-jwLOw/3CW4H6Vxcry4/buQHk7zm9Ne2YsidzTL1kpiMe4qqrRCwev3dkyWe2YkFmP+iZCQ7zku4KwjcLRoh8ew==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-jwLOw/3CW4H6Vxcry4/buQHk7zm9Ne2YsidzTL1kpiMe4qqrRCwev3dkyWe2YkFmP+iZCQ7zku4KwjcLRoh8ew==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@oxfmt/binding-linux-arm-musleabihf@0.42.0':
- resolution:
- {
- integrity: sha512-XwXu2vkMtiq2h7tfvN+WA/9/5/1IoGAVCFPiiQUvcAuG3efR97KNcRGM8BetmbYouFotQ2bDal3yyjUx6IPsTg==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-XwXu2vkMtiq2h7tfvN+WA/9/5/1IoGAVCFPiiQUvcAuG3efR97KNcRGM8BetmbYouFotQ2bDal3yyjUx6IPsTg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@oxfmt/binding-linux-arm64-gnu@0.42.0':
- resolution:
- {
- integrity: sha512-ea7s/XUJoT7ENAtUQDudFe3nkSM3e3Qpz4nJFRdzO2wbgXEcjnchKLEsV3+t4ev3r8nWxIYr9NRjPWtnyIFJVA==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-ea7s/XUJoT7ENAtUQDudFe3nkSM3e3Qpz4nJFRdzO2wbgXEcjnchKLEsV3+t4ev3r8nWxIYr9NRjPWtnyIFJVA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@oxfmt/binding-linux-arm64-musl@0.42.0':
- resolution:
- {
- integrity: sha512-+JA0YMlSdDqmacygGi2REp57c3fN+tzARD8nwsukx9pkCHK+6DkbAA9ojS4lNKsiBjIW8WWa0pBrBWhdZEqfuw==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-+JA0YMlSdDqmacygGi2REp57c3fN+tzARD8nwsukx9pkCHK+6DkbAA9ojS4lNKsiBjIW8WWa0pBrBWhdZEqfuw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@oxfmt/binding-linux-ppc64-gnu@0.42.0':
- resolution:
- {
- integrity: sha512-VfnET0j4Y5mdfCzh5gBt0NK28lgn5DKx+8WgSMLYYeSooHhohdbzwAStLki9pNuGy51y4I7IoW8bqwAaCMiJQg==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-VfnET0j4Y5mdfCzh5gBt0NK28lgn5DKx+8WgSMLYYeSooHhohdbzwAStLki9pNuGy51y4I7IoW8bqwAaCMiJQg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@oxfmt/binding-linux-riscv64-gnu@0.42.0':
- resolution:
- {
- integrity: sha512-gVlCbmBkB0fxBWbhBj9rcxezPydsQHf4MFKeHoTSPicOQ+8oGeTQgQ8EeesSybWeiFPVRx3bgdt4IJnH6nOjAA==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-gVlCbmBkB0fxBWbhBj9rcxezPydsQHf4MFKeHoTSPicOQ+8oGeTQgQ8EeesSybWeiFPVRx3bgdt4IJnH6nOjAA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@oxfmt/binding-linux-riscv64-musl@0.42.0':
- resolution:
- {
- integrity: sha512-zN5OfstL0avgt/IgvRu0zjQzVh/EPkcLzs33E9LMAzpqlLWiPWeMDZyMGFlSRGOdDjuNmlZBCgj0pFnK5u32TQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-zN5OfstL0avgt/IgvRu0zjQzVh/EPkcLzs33E9LMAzpqlLWiPWeMDZyMGFlSRGOdDjuNmlZBCgj0pFnK5u32TQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@oxfmt/binding-linux-s390x-gnu@0.42.0':
- resolution:
- {
- integrity: sha512-9X6+H2L0qMc2sCAgO9HS03bkGLMKvOFjmEdchaFlany3vNZOjnVui//D8k/xZAtQv2vaCs1reD5KAgPoIU4msA==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-9X6+H2L0qMc2sCAgO9HS03bkGLMKvOFjmEdchaFlany3vNZOjnVui//D8k/xZAtQv2vaCs1reD5KAgPoIU4msA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@oxfmt/binding-linux-x64-gnu@0.42.0':
- resolution:
- {
- integrity: sha512-BajxJ6KQvMMdpXGPWhBGyjb2Jvx4uec0w+wi6TJZ6Tv7+MzPwe0pO8g5h1U0jyFgoaF7mDl6yKPW3ykWcbUJRw==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-BajxJ6KQvMMdpXGPWhBGyjb2Jvx4uec0w+wi6TJZ6Tv7+MzPwe0pO8g5h1U0jyFgoaF7mDl6yKPW3ykWcbUJRw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@oxfmt/binding-linux-x64-musl@0.42.0':
- resolution:
- {
- integrity: sha512-0wV284I6vc5f0AqAhgAbHU2935B4bVpncPoe5n/WzVZY/KnHgqxC8iSFGeSyLWEgstFboIcWkOPck7tqbdHkzA==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-0wV284I6vc5f0AqAhgAbHU2935B4bVpncPoe5n/WzVZY/KnHgqxC8iSFGeSyLWEgstFboIcWkOPck7tqbdHkzA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@oxfmt/binding-openharmony-arm64@0.42.0':
- resolution:
- {
- integrity: sha512-p4BG6HpGnhfgHk1rzZfyR6zcWkE7iLrWxyehHfXUy4Qa5j3e0roglFOdP/Nj5cJJ58MA3isQ5dlfkW2nNEpolw==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-p4BG6HpGnhfgHk1rzZfyR6zcWkE7iLrWxyehHfXUy4Qa5j3e0roglFOdP/Nj5cJJ58MA3isQ5dlfkW2nNEpolw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@oxfmt/binding-win32-arm64-msvc@0.42.0':
- resolution:
- {
- integrity: sha512-mn//WV60A+IetORDxYieYGAoQso4KnVRRjORDewMcod4irlRe0OSC7YPhhwaexYNPQz/GCFk+v9iUcZ2W22yxQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-mn//WV60A+IetORDxYieYGAoQso4KnVRRjORDewMcod4irlRe0OSC7YPhhwaexYNPQz/GCFk+v9iUcZ2W22yxQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@oxfmt/binding-win32-ia32-msvc@0.42.0':
- resolution:
- {
- integrity: sha512-3gWltUrvuz4LPJXWivoAxZ28Of2O4N7OGuM5/X3ubPXCEV8hmgECLZzjz7UYvSDUS3grfdccQwmjynm+51EFpw==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-3gWltUrvuz4LPJXWivoAxZ28Of2O4N7OGuM5/X3ubPXCEV8hmgECLZzjz7UYvSDUS3grfdccQwmjynm+51EFpw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ia32]
os: [win32]
'@oxfmt/binding-win32-x64-msvc@0.42.0':
- resolution:
- {
- integrity: sha512-Wg4TMAfQRL9J9AZevJ/ZNy3uyyDztDYQtGr4P8UyyzIhLhFrdSmz1J/9JT+rv0fiCDLaFOBQnj3f3K3+a5PzDQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-Wg4TMAfQRL9J9AZevJ/ZNy3uyyDztDYQtGr4P8UyyzIhLhFrdSmz1J/9JT+rv0fiCDLaFOBQnj3f3K3+a5PzDQ==}
+ 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/inflection@0.18.0':
- resolution:
- {
- integrity: sha512-gHfxI6l/+cE9zY5ea6BjVjNcb9VKIavqRXGVyw16Fy0n4HgCmxF8ccZFFPq46JXfakNSk3cPTHDYvvWQaAlIPA==,
- }
+ resolution: {integrity: sha512-gHfxI6l/+cE9zY5ea6BjVjNcb9VKIavqRXGVyw16Fy0n4HgCmxF8ccZFFPq46JXfakNSk3cPTHDYvvWQaAlIPA==}
'@pgpm/metaschema-modules@0.18.0':
- resolution:
- {
- integrity: sha512-bSawB8SJJWGRpQjazJR4MYL6zMmI7dqdUtmw9O9jj7NqGXbZDLqrfUYOzxDbThJ/4BI49fJba19Qiz94Ys/CCg==,
- }
+ resolution: {integrity: sha512-bSawB8SJJWGRpQjazJR4MYL6zMmI7dqdUtmw9O9jj7NqGXbZDLqrfUYOzxDbThJ/4BI49fJba19Qiz94Ys/CCg==}
'@pgpm/metaschema-schema@0.18.0':
- resolution:
- {
- integrity: sha512-joIBoYegI4ZQFFPW7w0HhNXMT+sXnmSytX8YBxGgi5XZqjE2aQdtZcWBIiwZa3MjqU7mYN2xlZt5gPmo/IzLMw==,
- }
+ resolution: {integrity: sha512-joIBoYegI4ZQFFPW7w0HhNXMT+sXnmSytX8YBxGgi5XZqjE2aQdtZcWBIiwZa3MjqU7mYN2xlZt5gPmo/IzLMw==}
'@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/verify@0.18.0':
- resolution:
- {
- integrity: sha512-7XtY+hj9CbYb0ZhD0LJRp+TErtYS2z0FZbdkMNM7UabEwz05VLlS/lXbdtn5hksbUq6dkIxwxQ2mfFpATPpqDQ==,
- }
+ resolution: {integrity: sha512-7XtY+hj9CbYb0ZhD0LJRp+TErtYS2z0FZbdkMNM7UabEwz05VLlS/lXbdtn5hksbUq6dkIxwxQ2mfFpATPpqDQ==}
'@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/types@17.6.2':
- resolution:
- {
- integrity: sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==,
- }
+ resolution: {integrity: sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==}
'@pgsql/utils@17.8.15':
- resolution:
- {
- integrity: sha512-Q9szjg1ztXUhyoi49wt1kJvO/H+ohtaKXpkGxVlAlpmxh4/t7AXt1ldQX/LeKrlVqnisHrEKP9XgvR02pq+1oQ==,
- }
+ resolution: {integrity: sha512-Q9szjg1ztXUhyoi49wt1kJvO/H+ohtaKXpkGxVlAlpmxh4/t7AXt1ldQX/LeKrlVqnisHrEKP9XgvR02pq+1oQ==}
'@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.58.2':
- resolution:
- {
- integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==,
- }
- engines: { node: '>=18' }
+ resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==}
+ 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.4':
- resolution:
- {
- integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==,
- }
+ resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
'@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.0':
- resolution:
- {
- integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==,
- }
+ resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
'@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.0':
- resolution:
- {
- integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==,
- }
+ resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
'@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': '*'
@@ -5336,10 +4437,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': '*'
@@ -5352,10 +4450,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
@@ -5364,10 +4459,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
@@ -5376,10 +4468,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': '*'
@@ -5392,10 +4481,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
@@ -5404,10 +4490,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': '*'
@@ -5420,10 +4503,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': '*'
@@ -5436,10 +4516,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
@@ -5448,10 +4525,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': '*'
@@ -5464,10 +4538,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
@@ -5476,10 +4547,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': '*'
@@ -5492,10 +4560,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': '*'
@@ -5508,10 +4573,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': '*'
@@ -5524,10 +4586,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': '*'
@@ -5540,10 +4599,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': '*'
@@ -5556,10 +4612,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': '*'
@@ -5572,10 +4625,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': '*'
@@ -5588,10 +4638,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
@@ -5600,10 +4647,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
@@ -5612,10 +4656,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': '*'
@@ -5628,10 +4669,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
@@ -5640,10 +4678,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
@@ -5652,10 +4687,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
@@ -5664,10 +4696,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
@@ -5676,10 +4705,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
@@ -5688,10 +4714,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
@@ -5700,10 +4723,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
@@ -5712,10 +4732,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': '*'
@@ -5728,10 +4745,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': '*'
@@ -5744,2567 +4758,1469 @@ 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==}
'@sinonjs/commons@3.0.1':
- resolution:
- {
- integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==,
- }
+ resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==}
'@sinonjs/fake-timers@15.1.1':
- resolution:
- {
- integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==,
- }
+ resolution: {integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==}
'@smithy/abort-controller@4.2.12':
- resolution:
- {
- integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==}
+ engines: {node: '>=18.0.0'}
'@smithy/chunked-blob-reader-native@4.2.3':
- resolution:
- {
- integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==}
+ engines: {node: '>=18.0.0'}
'@smithy/chunked-blob-reader@5.2.2':
- resolution:
- {
- integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==}
+ engines: {node: '>=18.0.0'}
'@smithy/config-resolver@4.4.11':
- resolution:
- {
- integrity: sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==}
+ engines: {node: '>=18.0.0'}
'@smithy/core@3.23.12':
- resolution:
- {
- integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==}
+ engines: {node: '>=18.0.0'}
'@smithy/credential-provider-imds@4.2.12':
- resolution:
- {
- integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==}
+ engines: {node: '>=18.0.0'}
'@smithy/eventstream-codec@4.2.12':
- resolution:
- {
- integrity: sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==}
+ engines: {node: '>=18.0.0'}
'@smithy/eventstream-serde-browser@4.2.12':
- resolution:
- {
- integrity: sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==}
+ engines: {node: '>=18.0.0'}
'@smithy/eventstream-serde-config-resolver@4.3.12':
- resolution:
- {
- integrity: sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==}
+ engines: {node: '>=18.0.0'}
'@smithy/eventstream-serde-node@4.2.12':
- resolution:
- {
- integrity: sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==}
+ engines: {node: '>=18.0.0'}
'@smithy/eventstream-serde-universal@4.2.12':
- resolution:
- {
- integrity: sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/fetch-http-handler@5.3.15':
- resolution:
- {
- integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==}
+ engines: {node: '>=18.0.0'}
'@smithy/hash-blob-browser@4.2.13':
- resolution:
- {
- integrity: sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==}
+ engines: {node: '>=18.0.0'}
'@smithy/hash-node@4.2.12':
- resolution:
- {
- integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==}
+ engines: {node: '>=18.0.0'}
'@smithy/hash-stream-node@4.2.12':
- resolution:
- {
- integrity: sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==}
+ engines: {node: '>=18.0.0'}
'@smithy/invalid-dependency@4.2.12':
- resolution:
- {
- integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==}
+ 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/is-array-buffer@4.2.2':
- resolution:
- {
- integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==}
+ engines: {node: '>=18.0.0'}
'@smithy/md5-js@4.2.12':
- resolution:
- {
- integrity: sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/middleware-content-length@4.2.12':
- resolution:
- {
- integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==}
+ engines: {node: '>=18.0.0'}
'@smithy/middleware-endpoint@4.4.25':
- resolution:
- {
- integrity: sha512-dqjLwZs2eBxIUG6Qtw8/YZ4DvzHGIf0DA18wrgtfP6a50UIO7e2nY0FPdcbv5tVJKqWCCU5BmGMOUwT7Puan+A==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-dqjLwZs2eBxIUG6Qtw8/YZ4DvzHGIf0DA18wrgtfP6a50UIO7e2nY0FPdcbv5tVJKqWCCU5BmGMOUwT7Puan+A==}
+ engines: {node: '>=18.0.0'}
'@smithy/middleware-endpoint@4.4.27':
- resolution:
- {
- integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==}
+ engines: {node: '>=18.0.0'}
'@smithy/middleware-retry@4.4.42':
- resolution:
- {
- integrity: sha512-vbwyqHRIpIZutNXZpLAozakzamcINaRCpEy1MYmK6xBeW3xN+TyPRA123GjXnuxZIjc9848MRRCugVMTXxC4Eg==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-vbwyqHRIpIZutNXZpLAozakzamcINaRCpEy1MYmK6xBeW3xN+TyPRA123GjXnuxZIjc9848MRRCugVMTXxC4Eg==}
+ engines: {node: '>=18.0.0'}
'@smithy/middleware-serde@4.2.15':
- resolution:
- {
- integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==}
+ engines: {node: '>=18.0.0'}
'@smithy/middleware-stack@4.2.12':
- resolution:
- {
- integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==}
+ engines: {node: '>=18.0.0'}
'@smithy/node-config-provider@4.3.12':
- resolution:
- {
- integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==}
+ engines: {node: '>=18.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/property-provider@4.2.12':
- resolution:
- {
- integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==}
+ engines: {node: '>=18.0.0'}
'@smithy/protocol-http@5.3.12':
- resolution:
- {
- integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==}
+ engines: {node: '>=18.0.0'}
'@smithy/querystring-builder@4.2.12':
- resolution:
- {
- integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==}
+ engines: {node: '>=18.0.0'}
'@smithy/querystring-parser@4.2.12':
- resolution:
- {
- integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==}
+ engines: {node: '>=18.0.0'}
'@smithy/service-error-classification@4.2.12':
- resolution:
- {
- integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/shared-ini-file-loader@4.4.7':
- resolution:
- {
- integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==}
+ engines: {node: '>=18.0.0'}
'@smithy/signature-v4@5.3.12':
- resolution:
- {
- integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==}
+ engines: {node: '>=18.0.0'}
'@smithy/smithy-client@4.12.5':
- resolution:
- {
- integrity: sha512-UqwYawyqSr/aog8mnLnfbPurS0gi4G7IYDcD28cUIBhsvWs1+rQcL2IwkUQ+QZ7dibaoRzhNF99fAQ9AUcO00w==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-UqwYawyqSr/aog8mnLnfbPurS0gi4G7IYDcD28cUIBhsvWs1+rQcL2IwkUQ+QZ7dibaoRzhNF99fAQ9AUcO00w==}
+ engines: {node: '>=18.0.0'}
'@smithy/smithy-client@4.12.7':
- resolution:
- {
- integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/types@4.13.1':
- resolution:
- {
- integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==}
+ engines: {node: '>=18.0.0'}
'@smithy/url-parser@4.2.12':
- resolution:
- {
- integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-base64@4.3.2':
- resolution:
- {
- integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-body-length-browser@4.2.2':
- resolution:
- {
- integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-body-length-node@4.2.3':
- resolution:
- {
- integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==}
+ 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-buffer-from@4.2.2':
- resolution:
- {
- integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-config-provider@4.2.2':
- resolution:
- {
- integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-defaults-mode-browser@4.3.41':
- resolution:
- {
- integrity: sha512-M1w1Ux0rSVvBOxIIiqbxvZvhnjQ+VUjJrugtORE90BbadSTH+jsQL279KRL3Hv0w69rE7EuYkV/4Lepz/NBW9g==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-M1w1Ux0rSVvBOxIIiqbxvZvhnjQ+VUjJrugtORE90BbadSTH+jsQL279KRL3Hv0w69rE7EuYkV/4Lepz/NBW9g==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-defaults-mode-node@4.2.44':
- resolution:
- {
- integrity: sha512-YPze3/lD1KmWuZsl9JlfhcgGLX7AXhSoaCDtiPntUjNW5/YY0lOHjkcgxyE9x/h5vvS1fzDifMGjzqnNlNiqOQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-YPze3/lD1KmWuZsl9JlfhcgGLX7AXhSoaCDtiPntUjNW5/YY0lOHjkcgxyE9x/h5vvS1fzDifMGjzqnNlNiqOQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-endpoints@3.3.3':
- resolution:
- {
- integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-hex-encoding@4.2.2':
- resolution:
- {
- integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-middleware@4.2.12':
- resolution:
- {
- integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-retry@4.2.12':
- resolution:
- {
- integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-stream@4.5.20':
- resolution:
- {
- integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-uri-escape@4.2.2':
- resolution:
- {
- integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==}
+ engines: {node: '>=18.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'}
'@smithy/util-utf8@4.2.2':
- resolution:
- {
- integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==}
+ engines: {node: '>=18.0.0'}
'@smithy/util-waiter@4.2.13':
- resolution:
- {
- integrity: sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==}
+ engines: {node: '>=18.0.0'}
'@smithy/uuid@1.1.2':
- resolution:
- {
- integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==,
- }
- engines: { node: '>=18.0.0' }
+ resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==}
+ engines: {node: '>=18.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@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: '*'
'@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.1':
- resolution:
- {
- integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==,
- }
+ resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
'@tybys/wasm-util@0.9.0':
- resolution:
- {
- integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==,
- }
+ resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
'@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/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/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/interpret@1.1.4':
- resolution:
- {
- integrity: sha512-r+tPKWHYqaxJOYA3Eik0mMi+SEREqOXLmsooRFmc6GHv7nWUDixFtKN+cegvsPlDcEZd9wxsdp041v2imQuvag==,
- }
+ resolution: {integrity: sha512-r+tPKWHYqaxJOYA3Eik0mMi+SEREqOXLmsooRFmc6GHv7nWUDixFtKN+cegvsPlDcEZd9wxsdp041v2imQuvag==}
'@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/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/multer@2.1.0':
- resolution:
- {
- integrity: sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA==,
- }
+ resolution: {integrity: sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA==}
'@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@25.5.0':
- resolution:
- {
- integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==,
- }
+ resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
'@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.18.0':
- resolution:
- {
- integrity: sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==,
- }
+ resolution: {integrity: sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==}
'@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/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.12':
- resolution:
- {
- integrity: sha512-IBemrqI6nzvbgwE41Lnd4v4Yf1Kc7F1UHjk1GFBLNhLcI/Zop1ggHQ8g7Y8QYc6jGVgzWQcsa0MBNcGnDY9UGw==,
- }
+ resolution: {integrity: sha512-IBemrqI6nzvbgwE41Lnd4v4Yf1Kc7F1UHjk1GFBLNhLcI/Zop1ggHQ8g7Y8QYc6jGVgzWQcsa0MBNcGnDY9UGw==}
'@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/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.57.0':
- resolution:
- {
- integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.57.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/parser@8.57.0':
- resolution:
- {
- integrity: sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==}
+ 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.0.0'
'@typescript-eslint/project-service@8.57.0':
- resolution:
- {
- integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/scope-manager@8.57.0':
- resolution:
- {
- integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.57.0':
- resolution:
- {
- integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.57.0':
- resolution:
- {
- integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==}
+ 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.0.0'
'@typescript-eslint/types@8.57.0':
- resolution:
- {
- integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.57.0':
- resolution:
- {
- integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/utils@8.57.0':
- resolution:
- {
- integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==}
+ 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.0.0'
'@typescript-eslint/visitor-keys@8.57.0':
- resolution:
- {
- integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==,
- }
- engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.0':
- resolution:
- {
- integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==,
- }
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
- resolution:
- {
- integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==,
- }
+ resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
cpu: [arm]
os: [android]
'@unrs/resolver-binding-android-arm64@1.11.1':
- resolution:
- {
- integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==,
- }
+ resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==}
cpu: [arm64]
os: [android]
'@unrs/resolver-binding-darwin-arm64@1.11.1':
- resolution:
- {
- integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==,
- }
+ resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==}
cpu: [arm64]
os: [darwin]
'@unrs/resolver-binding-darwin-x64@1.11.1':
- resolution:
- {
- integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==,
- }
+ resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==}
cpu: [x64]
os: [darwin]
'@unrs/resolver-binding-freebsd-x64@1.11.1':
- resolution:
- {
- integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==,
- }
+ resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==}
cpu: [x64]
os: [freebsd]
'@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
- resolution:
- {
- integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==,
- }
+ resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==}
cpu: [arm]
os: [linux]
'@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
- resolution:
- {
- integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==,
- }
+ resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==}
cpu: [arm]
os: [linux]
'@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
- resolution:
- {
- integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==,
- }
+ resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
- resolution:
- {
- integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==,
- }
+ resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
- resolution:
- {
- integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==,
- }
+ resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
- resolution:
- {
- integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==,
- }
+ resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
- resolution:
- {
- integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==,
- }
+ resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
- resolution:
- {
- integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==,
- }
+ resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
- resolution:
- {
- integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==,
- }
+ resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
- resolution:
- {
- integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==,
- }
+ resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
- resolution:
- {
- integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==,
- }
- engines: { node: '>=14.0.0' }
+ resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
+ engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
- resolution:
- {
- integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==,
- }
+ resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==}
cpu: [arm64]
os: [win32]
'@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
- resolution:
- {
- integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==,
- }
+ resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==}
cpu: [ia32]
os: [win32]
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
- resolution:
- {
- integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==,
- }
+ resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==}
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}
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.18.0:
- resolution:
- {
- integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==,
- }
+ resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
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'}
append-field@1.0.0:
- resolution:
- {
- integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==,
- }
+ resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
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'}
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.3.0:
- resolution:
- {
- integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==}
+ 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@5.0.4:
- resolution:
- {
- integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==,
- }
- engines: { node: 18 || 20 || >=22 }
+ resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==}
+ 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'}
busboy@1.6.0:
- resolution:
- {
- integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==,
- }
- engines: { node: '>=10.16.0' }
+ resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+ engines: {node: '>=10.16.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-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:
@@ -8312,134 +6228,74 @@ 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-fetch@4.1.0:
- resolution:
- {
- integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==,
- }
+ resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==}
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
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.0:
- resolution:
- {
- integrity: sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==,
- }
- engines: { node: '>= 10' }
+ resolution: {integrity: sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==}
+ 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'}
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:
@@ -8447,31 +6303,19 @@ 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'}
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:
@@ -8479,10 +6323,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:
@@ -8490,230 +6331,125 @@ 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==}
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.1:
- resolution:
- {
- integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==,
- }
+ resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==}
peerDependencies:
'@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=4'
@@ -8805,270 +6541,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
@@ -9077,39 +6696,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: '*'
@@ -9118,190 +6722,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.3.0:
- resolution:
- {
- integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==}
+ 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.0:
- resolution:
- {
- integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==,
- }
+ resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
fast-xml-builder@1.1.4:
- resolution:
- {
- integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==,
- }
+ resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==}
fast-xml-parser@5.5.8:
- resolution:
- {
- integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==,
- }
+ resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==}
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:
@@ -9309,92 +6829,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:
@@ -9402,38 +6883,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
@@ -9447,299 +6913,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.3.9:
- resolution:
- {
- integrity: sha512-zPJ+qH993GtbtsQSCd283ErOLF7/4cg5+DGVYHK9laiX8lITF4x0DJMktxAHqgmMdI3QjLuMOcGnTGOH950sFg==,
- }
+ resolution: {integrity: sha512-zPJ+qH993GtbtsQSCd283ErOLF7/4cg5+DGVYHK9laiX8lITF4x0DJMktxAHqgmMdI3QjLuMOcGnTGOH950sFg==}
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.0:
- resolution:
- {
- integrity: sha512-V4AhdcQhgDDqKZS708WWu8iC6Jd80gVca6zC1M8YUb8gZOOS4r0f/V89KbGFWh0nuLaZQeFj+LZ9Ps9B8F2LEA==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-V4AhdcQhgDDqKZS708WWu8iC6Jd80gVca6zC1M8YUb8gZOOS4r0f/V89KbGFWh0nuLaZQeFj+LZ9Ps9B8F2LEA==}
+ engines: {node: '>=22'}
peerDependencies:
'@envelop/core': ^5.0.0
graphql: 16.13.0
@@ -9748,11 +7088,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
@@ -9775,11 +7112,8 @@ packages:
optional: true
graphile-build-pg@5.0.0:
- resolution:
- {
- integrity: sha512-a0FAR5n8UIYMAI1URIuWAAb+dZUDNrP09rYdg7veBUhJQyBtfkUHGNwZ+gDEhRBOvr/eRrw5Jkqc+Moi+XwW8A==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-a0FAR5n8UIYMAI1URIuWAAb+dZUDNrP09rYdg7veBUhJQyBtfkUHGNwZ+gDEhRBOvr/eRrw5Jkqc+Moi+XwW8A==}
+ engines: {node: '>=22'}
peerDependencies:
'@dataplan/pg': ^1.0.0-rc.7
grafast: ^1.0.0-rc.8
@@ -9794,29 +7128,20 @@ packages:
optional: true
graphile-build@5.0.0:
- resolution:
- {
- integrity: sha512-hGieff6/UaikT7ywWv2XTFa1mGJ1Zdytqbfw0bmVlXWMOeJGpvCdx9+k5Kpw7aIZ92twPa5yb2HUo0Q8j2Kwzw==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-hGieff6/UaikT7ywWv2XTFa1mGJ1Zdytqbfw0bmVlXWMOeJGpvCdx9+k5Kpw7aIZ92twPa5yb2HUo0Q8j2Kwzw==}
+ 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.0:
- resolution:
- {
- integrity: sha512-nPKrrpmYT/cMibqHnNL+zLIRrC/SQhop7yV4tZiyrC/C0mckdlghRWR9oPV7UppkeFIdgTt5/7UQCrwhX82faQ==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-nPKrrpmYT/cMibqHnNL+zLIRrC/SQhop7yV4tZiyrC/C0mckdlghRWR9oPV7UppkeFIdgTt5/7UQCrwhX82faQ==}
+ 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
@@ -9830,56 +7155,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.7:
- resolution:
- {
- integrity: sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg==,
- }
- engines: { node: '>=20' }
+ resolution: {integrity: sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg==}
+ engines: {node: '>=20'}
peerDependencies:
'@fastify/websocket': ^10 || ^11
crossws: ~0.3
@@ -9894,623 +7201,350 @@ 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.8:
- resolution:
- {
- integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==,
- }
- engines: { node: '>=0.4.7' }
+ resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
+ 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-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.7.0:
- resolution:
- {
- integrity: sha512-Gp6Bd7NGeA1y/vV+q0Dl7KqakMHvHiTXyRIMYXCH4L6tQ3AWJBWd6BRmxikOp9t1C8eoJIGrHs7iJt34hx573Q==,
- }
+ resolution: {integrity: sha512-Gp6Bd7NGeA1y/vV+q0Dl7KqakMHvHiTXyRIMYXCH4L6tQ3AWJBWd6BRmxikOp9t1C8eoJIGrHs7iJt34hx573Q==}
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-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.3.0:
- resolution:
- {
- integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-circus@30.3.0:
- resolution:
- {
- integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-cli@30.3.0:
- resolution:
- {
- integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==}
+ 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
@@ -10519,11 +7553,8 @@ packages:
optional: true
jest-config@30.3.0:
- resolution:
- {
- integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
peerDependencies:
'@types/node': '*'
esbuild-register: '>=3.4.0'
@@ -10537,123 +7568,72 @@ 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.3.0:
- resolution:
- {
- integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-docblock@30.2.0:
- resolution:
- {
- integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-each@30.3.0:
- resolution:
- {
- integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-environment-node@30.3.0:
- resolution:
- {
- integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==}
+ 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:
@@ -10661,88 +7641,52 @@ 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-resolve-dependencies@30.3.0:
- resolution:
- {
- integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-resolve@30.3.0:
- resolution:
- {
- integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-runner@30.3.0:
- resolution:
- {
- integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-runtime@30.3.0:
- resolution:
- {
- integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-snapshot@30.3.0:
- resolution:
- {
- integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==}
+ 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.3.0:
- resolution:
- {
- integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-validate@30.3.0:
- resolution:
- {
- integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-watcher@30.3.0:
- resolution:
- {
- integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest-worker@30.3.0:
- resolution:
- {
- integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
jest@30.3.0:
- resolution:
- {
- integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==}
+ 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
@@ -10751,601 +7695,328 @@ packages:
optional: true
jiti@2.6.1:
- resolution:
- {
- integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==,
- }
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
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
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.17.23:
- resolution:
- {
- integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==,
- }
+ resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
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.1.12:
- resolution:
- {
- integrity: sha512-R3bITl50Ts2GzoaErywe8n24Iu2qbvbNOqOyidjDjh6iqK0CAj2VzIk3xRS4z8Q4xDQzaJrcb2+dGDjqRj6ChA==,
- }
+ resolution: {integrity: sha512-R3bITl50Ts2GzoaErywe8n24Iu2qbvbNOqOyidjDjh6iqK0CAj2VzIk3xRS4z8Q4xDQzaJrcb2+dGDjqRj6ChA==}
hasBin: true
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@0.3.0:
- resolution:
- {
- integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==,
- }
- engines: { node: '>= 0.6' }
+ resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+ engines: {node: '>= 0.6'}
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:
@@ -11353,584 +8024,320 @@ 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@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==}
multer@2.1.1:
- resolution:
- {
- integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==,
- }
- engines: { node: '>= 10.16.0' }
+ resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==}
+ engines: {node: '>= 10.16.0'}
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.1.10:
- resolution:
- {
- integrity: sha512-5V2kUPrBee/tmoS2p0IJ35BcaJuW1p1yXF5GP8JpXIkDoPbaYeYypAHizUeZkAUxcC7Rago7izWmEq7qa8+Mhw==,
- }
+ resolution: {integrity: sha512-5V2kUPrBee/tmoS2p0IJ35BcaJuW1p1yXF5GP8JpXIkDoPbaYeYypAHizUeZkAUxcC7Rago7izWmEq7qa8+Mhw==}
nested-obj@0.1.5:
- resolution:
- {
- integrity: sha512-04Y7qDMlI8RbYTn0cJAKaw/mLrO9UmLj3xbrjTZKDfOn9f3b/RXEQFIIpveJlwn8KfPwdVFWLZUaL5gNuQ7G0w==,
- }
+ resolution: {integrity: sha512-04Y7qDMlI8RbYTn0cJAKaw/mLrO9UmLj3xbrjTZKDfOn9f3b/RXEQFIIpveJlwn8KfPwdVFWLZUaL5gNuQ7G0w==}
nested-obj@0.2.1:
- resolution:
- {
- integrity: sha512-MQnXdT8qoxxu5/ONQ8tO70HsuvuUAhLmAvOr1RaAtWqpGda+JycVIhN1Pclq5Zny7sr4Jn4wKgIR8IpdnXU+EQ==,
- }
+ resolution: {integrity: sha512-MQnXdT8qoxxu5/ONQ8tO70HsuvuUAhLmAvOr1RaAtWqpGda+JycVIhN1Pclq5Zny7sr4Jn4wKgIR8IpdnXU+EQ==}
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:
@@ -11938,11 +8345,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:
@@ -11950,180 +8354,102 @@ 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@7.0.13:
- resolution:
- {
- integrity: sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==,
- }
- engines: { node: '>=6.0.0' }
+ resolution: {integrity: sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==}
+ 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==}
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
@@ -12135,437 +8461,245 @@ 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.42.0:
- resolution:
- {
- integrity: sha512-QhejGErLSMReNuZ6vxgFHDyGoPbjTRNi6uGHjy0cvIjOQFqD6xmr/T+3L41ixR3NIgzcNiJ6ylQKpvShTgDfqg==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ resolution: {integrity: sha512-QhejGErLSMReNuZ6vxgFHDyGoPbjTRNi6uGHjy0cvIjOQFqD6xmr/T+3L41ixR3NIgzcNiJ6ylQKpvShTgDfqg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
hasBin: 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.2.0:
- resolution:
- {
- integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==,
- }
- engines: { node: '>=14.0.0' }
+ resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==}
+ 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-connection-string@2.12.0:
- resolution:
- {
- integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==,
- }
+ resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==}
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.0:
- resolution:
- {
- integrity: sha512-5Q+wTTbPO0+yVWwC8Qh58b12HOqXgE6Si+Xq17+QBngpJ2u6yZvhRxHlrSGimqAMWLIXkV6/FvCTqwcQ1IBvAw==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-5Q+wTTbPO0+yVWwC8Qh58b12HOqXgE6Si+Xq17+QBngpJ2u6yZvhRxHlrSGimqAMWLIXkV6/FvCTqwcQ1IBvAw==}
+ 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-proto-parser@1.30.5:
- resolution:
- {
- integrity: sha512-DWXRF5u3hcAwJfrlfKjqOfSex2E6d4Lh9Y3NxFsaieQD1ZoQlsceAn1Xp6C5HRKnHxUB/F5N7R4aVeDBG/sk4Q==,
- }
+ resolution: {integrity: sha512-DWXRF5u3hcAwJfrlfKjqOfSex2E6d4Lh9Y3NxFsaieQD1ZoQlsceAn1Xp6C5HRKnHxUB/F5N7R4aVeDBG/sk4Q==}
pg-protocol@1.13.0:
- resolution:
- {
- integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==,
- }
+ resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==}
pg-sql2@5.0.0:
- resolution:
- {
- integrity: sha512-gvmfl0XeOeFjd+1aH5uIp1eZxUM6LmaMP8yy1EWE16XaPeUP8dhKdHtdHc0MsX0ZgCy+1g67yS1HCYWns2TdmQ==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-gvmfl0XeOeFjd+1aH5uIp1eZxUM6LmaMP8yy1EWE16XaPeUP8dhKdHtdHc0MsX0ZgCy+1g67yS1HCYWns2TdmQ==}
+ 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:
@@ -12573,141 +8707,81 @@ packages:
optional: true
pgpass@1.0.5:
- resolution:
- {
- integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==,
- }
+ resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
pgsql-deparser@17.18.2:
- resolution:
- {
- integrity: sha512-mnoT6ti7IFLwSUxe0UkxMjfUtyyWmwClf2sJyLbbRGbZ53SgiT493INFyxmeRvQxc99lmpz6aCxUnjj0ZhGlJA==,
- }
+ resolution: {integrity: sha512-mnoT6ti7IFLwSUxe0UkxMjfUtyyWmwClf2sJyLbbRGbZ53SgiT493INFyxmeRvQxc99lmpz6aCxUnjj0ZhGlJA==}
pgsql-parser@17.9.14:
- resolution:
- {
- integrity: sha512-2qhO2DXkIbqtRdXN4dj8dD/RmSRtNfWxK08dYQ630WwJe1AF6ExuDV0zYGN8BR2NhCHqWmU3qKqJJPBXdib5DQ==,
- }
+ resolution: {integrity: sha512-2qhO2DXkIbqtRdXN4dj8dD/RmSRtNfWxK08dYQ630WwJe1AF6ExuDV0zYGN8BR2NhCHqWmU3qKqJJPBXdib5DQ==}
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'}
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.58.2:
- resolution:
- {
- integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==,
- }
- engines: { node: '>=18' }
+ resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==}
+ engines: {node: '>=18'}
hasBin: true
playwright@1.58.2:
- resolution:
- {
- integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==,
- }
- engines: { node: '>=18' }
+ resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==}
+ 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.0:
- resolution:
- {
- integrity: sha512-BEv+qrOQBgMtzq3xWhHzwD9+cTdFz8XG/x7e4qWd/4+oqIDrKzOX+OXhFW8vx7jeRnvCMQ3Y8DYe/4tS4DJjqA==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-BEv+qrOQBgMtzq3xWhHzwD9+cTdFz8XG/x7e4qWd/4+oqIDrKzOX+OXhFW8vx7jeRnvCMQ3Y8DYe/4tS4DJjqA==}
+ engines: {node: '>=22'}
hasBin: true
peerDependencies:
'@dataplan/json': 1.0.0
@@ -12727,126 +8801,72 @@ 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@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.3.0:
- resolution:
- {
- integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==}
+ 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:
@@ -12854,156 +8874,87 @@ 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-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.4:
- resolution:
- {
- integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==,
- }
+ resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==}
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: '*'
@@ -13015,18 +8966,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
@@ -13035,11 +8980,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
@@ -13048,11 +8990,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
@@ -13061,253 +9000,145 @@ packages:
optional: true
react-test-renderer@19.2.4:
- resolution:
- {
- integrity: sha512-Ttl5D7Rnmi6JGMUpri4UjB4BAN0FPs4yRDnu2XSsigCWOLm11o8GwRlVsh27ER+4WFqsGtrBuuv5zumUaRCmKw==,
- }
+ resolution: {integrity: sha512-Ttl5D7Rnmi6JGMUpri4UjB4BAN0FPs4yRDnu2XSsigCWOLm11o8GwRlVsh27ER+4WFqsGtrBuuv5zumUaRCmKw==}
peerDependencies:
react: ^19.2.4
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'}
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
@@ -13319,39 +9150,24 @@ 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'}
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
@@ -13363,11 +9179,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
@@ -13381,703 +9194,394 @@ 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==}
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
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.1:
- resolution:
- {
- integrity: sha512-zlUXA6n3HkO0jMyNNc2S67uw7DWHOoLU9vjPo5oW2c8ehJMpRlSumyw4riuvfWPfW/8mryd7ED5PVf4YVg8Y6w==,
- }
- engines: { node: '>=18.18.0' }
+ resolution: {integrity: sha512-zlUXA6n3HkO0jMyNNc2S67uw7DWHOoLU9vjPo5oW2c8ehJMpRlSumyw4riuvfWPfW/8mryd7ED5PVf4YVg8Y6w==}
+ 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'}
streamsearch@1.1.0:
- resolution:
- {
- integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==,
- }
- engines: { node: '>=10.0.0' }
+ resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+ engines: {node: '>=10.0.0'}
strfy-js@3.2.1:
- resolution:
- {
- integrity: sha512-HSw2lkUJVPZ75I+E3HM7UqHMKvBCwjRt1MIAxPPNtLFjuqCrnDVKQQGfotdj/3qHxuhB6NDQ1rYmNjVpPBiNEA==,
- }
+ resolution: {integrity: sha512-HSw2lkUJVPZ75I+E3HM7UqHMKvBCwjRt1MIAxPPNtLFjuqCrnDVKQQGfotdj/3qHxuhB6NDQ1rYmNjVpPBiNEA==}
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.2.0:
- resolution:
- {
- integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==,
- }
+ resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==}
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'}
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.0:
- resolution:
- {
- integrity: sha512-Ry2HVNPnFW6yzNALT+LuABIg2YiTf9orzSl2tCh2mfxLIl0LrnAyadmFDfANdQFzPbPW3Y1DY03QwDoCqJuc/A==,
- }
- engines: { node: '>=22' }
+ resolution: {integrity: sha512-Ry2HVNPnFW6yzNALT+LuABIg2YiTf9orzSl2tCh2mfxLIl0LrnAyadmFDfANdQFzPbPW3Y1DY03QwDoCqJuc/A==}
+ 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'}
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}
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
tr46@0.0.3:
- resolution:
- {
- integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==,
- }
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
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.4.0:
- resolution:
- {
- integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==,
- }
- engines: { node: '>=18.12' }
+ resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
+ engines: {node: '>=18.12'}
peerDependencies:
typescript: '>=4.8.4'
ts-jest@29.4.6:
- resolution:
- {
- integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==,
- }
- engines: { node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0 }
+ resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==}
+ 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'
@@ -14103,10 +9607,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'
@@ -14120,257 +9621,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@1.6.18:
- resolution:
- {
- integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==,
- }
- engines: { node: '>= 0.6' }
+ resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+ engines: {node: '>= 0.6'}
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.18.2:
- resolution:
- {
- integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==,
- }
+ resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
undici@7.24.6:
- resolution:
- {
- integrity: sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==,
- }
- engines: { node: '>=20.18.1' }
+ resolution: {integrity: sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==}
+ engines: {node: '>=20.18.1'}
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.11.1:
- resolution:
- {
- integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==,
- }
+ resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
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
@@ -14379,11 +9769,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
@@ -14392,72 +9779,42 @@ 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==}
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
@@ -14496,170 +9853,95 @@ packages:
optional: true
vscode-languageserver-types@3.17.5:
- resolution:
- {
- integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==,
- }
+ resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
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==}
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@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.19.0:
- resolution:
- {
- integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==,
- }
- engines: { node: '>=10.0.0' }
+ resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
+ engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
@@ -14670,113 +9952,65 @@ packages:
optional: true
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.2:
- resolution:
- {
- integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==,
- }
- engines: { node: '>= 14.6' }
+ resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
+ 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'
@@ -14793,6 +10027,7 @@ packages:
optional: true
snapshots:
+
'@0no-co/graphql.web@1.2.0(graphql@16.13.0)':
optionalDependencies:
graphql: 16.13.0
@@ -16209,6 +11444,41 @@ snapshots:
- supports-color
- ts-node
+ '@jest/core@30.3.0(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3))':
+ dependencies:
+ '@jest/console': 30.3.0
+ '@jest/pattern': 30.0.1
+ '@jest/reporters': 30.3.0
+ '@jest/test-result': 30.3.0
+ '@jest/transform': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 22.19.15
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ ci-info: 4.4.0
+ exit-x: 0.2.2
+ graceful-fs: 4.2.11
+ jest-changed-files: 30.3.0
+ jest-config: 30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3))
+ jest-haste-map: 30.3.0
+ jest-message-util: 30.3.0
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.3.0
+ jest-resolve-dependencies: 30.3.0
+ jest-runner: 30.3.0
+ jest-runtime: 30.3.0
+ jest-snapshot: 30.3.0
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ jest-watcher: 30.3.0
+ pretty-format: 30.3.0
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
'@jest/diff-sequences@30.0.1': {}
'@jest/diff-sequences@30.3.0': {}
@@ -18033,7 +13303,6 @@ snapshots:
'@types/node@25.5.0':
dependencies:
undici-types: 7.18.2
- optional: true
'@types/nodemailer@7.0.11':
dependencies:
@@ -20396,6 +15665,25 @@ snapshots:
- supports-color
- ts-node
+ jest-cli@30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3)):
+ dependencies:
+ '@jest/core': 30.3.0(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3))
+ '@jest/test-result': 30.3.0
+ '@jest/types': 30.3.0
+ chalk: 4.1.2
+ exit-x: 0.2.2
+ import-local: 3.2.0
+ jest-config: 30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3))
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
jest-config@30.3.0(@types/node@22.19.11)(ts-node@10.9.2(@types/node@22.19.11)(typescript@5.9.3)):
dependencies:
'@babel/core': 7.29.0
@@ -20460,6 +15748,70 @@ snapshots:
- babel-plugin-macros
- supports-color
+ jest-config@30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3)):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@jest/get-type': 30.1.0
+ '@jest/pattern': 30.0.1
+ '@jest/test-sequencer': 30.3.0
+ '@jest/types': 30.3.0
+ babel-jest: 30.3.0(@babel/core@7.29.0)
+ chalk: 4.1.2
+ ci-info: 4.4.0
+ deepmerge: 4.3.1
+ glob: 10.5.0
+ graceful-fs: 4.2.11
+ jest-circus: 30.3.0
+ jest-docblock: 30.2.0
+ jest-environment-node: 30.3.0
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.3.0
+ jest-runner: 30.3.0
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ parse-json: 5.2.0
+ pretty-format: 30.3.0
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ optionalDependencies:
+ '@types/node': 22.19.15
+ ts-node: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-config@30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3)):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@jest/get-type': 30.1.0
+ '@jest/pattern': 30.0.1
+ '@jest/test-sequencer': 30.3.0
+ '@jest/types': 30.3.0
+ babel-jest: 30.3.0(@babel/core@7.29.0)
+ chalk: 4.1.2
+ ci-info: 4.4.0
+ deepmerge: 4.3.1
+ glob: 10.5.0
+ graceful-fs: 4.2.11
+ jest-circus: 30.3.0
+ jest-docblock: 30.2.0
+ jest-environment-node: 30.3.0
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.3.0
+ jest-runner: 30.3.0
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ parse-json: 5.2.0
+ pretty-format: 30.3.0
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ optionalDependencies:
+ '@types/node': 25.5.0
+ ts-node: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
jest-diff@29.7.0:
dependencies:
chalk: 4.1.2
@@ -20740,6 +16092,19 @@ snapshots:
- supports-color
- ts-node
+ jest@30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3)):
+ dependencies:
+ '@jest/core': 30.3.0(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3))
+ '@jest/types': 30.3.0
+ import-local: 3.2.0
+ jest-cli: 30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3))
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
jiti@2.6.1: {}
js-beautify@1.15.4:
@@ -23183,6 +18548,26 @@ snapshots:
babel-jest: 30.3.0(@babel/core@7.29.0)
jest-util: 30.3.0
+ ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3)))(typescript@5.9.3):
+ dependencies:
+ bs-logger: 0.2.6
+ fast-json-stable-stringify: 2.1.0
+ handlebars: 4.7.8
+ jest: 30.3.0(@types/node@25.5.0)(ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3))
+ json5: 2.2.3
+ lodash.memoize: 4.1.2
+ make-error: 1.3.6
+ semver: 7.7.4
+ type-fest: 4.41.0
+ typescript: 5.9.3
+ yargs-parser: 21.1.1
+ optionalDependencies:
+ '@babel/core': 7.29.0
+ '@jest/transform': 30.3.0
+ '@jest/types': 30.3.0
+ babel-jest: 30.3.0(@babel/core@7.29.0)
+ jest-util: 30.3.0
+
ts-node@10.9.2(@types/node@22.19.11)(typescript@5.9.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
@@ -23201,14 +18586,14 @@ snapshots:
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
- ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3):
+ ts-node@10.9.2(@types/node@25.5.0)(typescript@5.9.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.12
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
- '@types/node': 22.19.15
+ '@types/node': 25.5.0
acorn: 8.15.0
acorn-walk: 8.3.4
arg: 4.1.3
@@ -23289,8 +18674,7 @@ snapshots:
undici-types@6.21.0: {}
- undici-types@7.18.2:
- optional: true
+ undici-types@7.18.2: {}
undici@7.24.6: {}
From 703e78bb6d4bbfa9a04991ed272f6136ac6d8bfa Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 21 Apr 2026 07:41:03 +0800
Subject: [PATCH 02/17] remove outdated file
---
graphile/graphile-multi-tenancy-cache/SPEC.md | 480 ------------------
1 file changed, 480 deletions(-)
delete mode 100644 graphile/graphile-multi-tenancy-cache/SPEC.md
diff --git a/graphile/graphile-multi-tenancy-cache/SPEC.md b/graphile/graphile-multi-tenancy-cache/SPEC.md
deleted file mode 100644
index a2d09348fd..0000000000
--- a/graphile/graphile-multi-tenancy-cache/SPEC.md
+++ /dev/null
@@ -1,480 +0,0 @@
-# graphile-multi-tenancy-cache — Implementation Spec
-
-## Problem
-
-Constructive's GraphQL server creates a **dedicated PostGraphile instance per tenant** (one `postgraphile()` call per unique `svc_key`). Each instance holds its own `PgRegistry`, `GraphQLSchema`, operation plan cache, and V8 closures — ~50–80 MB per tenant. At scale (hundreds of tenants), this leads to:
-
-- **Unbounded memory growth** — RSS grows linearly with tenant count
-- **Slow cold starts** — each new tenant triggers a full schema build (~200–400ms)
-- **LRU churn** — when tenant count exceeds `GRAPHILE_CACHE_MAX`, constant eviction/rebuild cycles tank QPS and spike latency
-
-## Solution
-
-A **template-based multi-tenancy cache** that shares a single PostGraphile instance across all tenants with structurally identical schemas. SQL is remapped per-request at the `client.query()` level — no Crystal modifications required.
-
-### Key invariant
-
-Constructive tenant schemas use the naming convention `t__` (e.g., `t_1_services_public`, `t_2_services_public`). These names **never collide** with table/column names (`apis`, `apps`, `domains`). This invariant is necessary but not sufficient for safety, so SQL remap uses AST-based rewrite with strict failure policy (see SQL Remap Safety Contract below), not best-effort raw text replacement.
-
----
-
-## Architecture
-
-### Request flow
-
-```
-Request (svc_key)
- │
- ├─ HIT ──► tenantInstances.get(svc_key) ──► inject sqlTextTransform ──► handler
- │
- └─ MISS ──► getOrCreateTenantInstance()
- │
- ├─ Introspect + fingerprint (cached)
- │
- ├─ Template exists for fingerprint?
- │ ├─ YES ──► reuse template, build schema remap transform
- │ └─ NO ──► build new template (single-flight), register
- │
- └─ Return TenantInstance { handler, sqlTextTransform, pgSettings }
-```
-
-### SQL interception (wrapper approach)
-
-```
-PgContextPlugin (Crystal, runs first in prepareArgs)
- contextValue["withPgClient"] = withPgClientFromPgService(…)
- │
-PgMultiTenancyWrapperPlugin (this package, runs AFTER)
- contextValue["withPgClient"] = wrap(original, contextValue)
- │
-PgExecutor reads ctx.get("withPgClient") at execution time
- → gets wrapped version
- → client.query({ text }) passes through Proxy
- → SQL text transformed: "t_1_services_public" → "t_2_services_public"
- → PostgreSQL receives tenant-correct SQL
-```
-
-The transform is read **lazily** at call time (not at middleware time) because `grafast.context` finalization happens after middleware.
-
-### Three cache layers
-
-| Layer | Key | Value | Eviction |
-|---|---|---|---|
-| **Tenant Instance** | `svc_key` | `TenantInstance` (handler + transform) | Package-owned: `flushTenantInstance()`, flush via LISTEN/NOTIFY |
-| **Introspection** | `connHash:schema1,schema2` | Parsed introspection + fingerprint | LRU (max 100) + TTL (30min idle) |
-| **Template** | SHA-256 fingerprint | PostGraphile instance (pgl + handler + httpServer) | LRU (max 50) + TTL (30min idle) + refCount protection |
-
----
-
-## Folder structure
-
-### New package: `graphile/graphile-multi-tenancy-cache/`
-
-```
-graphile/graphile-multi-tenancy-cache/
-├── SPEC.md ← this file
-├── package.json
-├── jest.config.js
-├── tsconfig.json
-├── tsconfig.esm.json
-└── src/
- ├── index.ts ← public API exports
- │
- │ # Core modules
- ├── pg-client-wrapper-plugin.ts ← Grafast middleware (SQL interception via Proxy)
- ├── multi-tenancy-cache.ts ← orchestrator (full lifecycle: tenant instances, templates, shutdown)
- ├── registry-template-map.ts ← template registry (LRU/TTL eviction, refCount)
- ├── introspection-cache.ts ← introspection cache (LRU/TTL eviction)
- │
- │ # Utilities
- ├── utils/
- │ ├── fingerprint.ts ← SHA-256 structural fingerprint (schema-name-agnostic)
- │ ├── sql-transform.ts ← SQL remap orchestrator (AST rewrite + hot-path cache)
- │ ├── schema-map.ts ← buildSchemaMap, buildTenantPgSettings, remapSchemas
- │ └── introspection-query.ts ← fetchIntrospection, parseIntrospection (raw pg_catalog access)
- │
- │ # Tests
- └── __tests__/
- ├── pg-client-wrapper-plugin.test.ts
- ├── registry-template-map.test.ts
- ├── introspection-cache.test.ts
- ├── fingerprint.test.ts
- ├── sql-transform.test.ts
- ├── schema-map.test.ts
- └── single-flight.test.ts
-```
-
-### Modified files in existing packages
-
-```
-graphql/server/src/middleware/
-├── graphile.ts ← add multiTenancyHandler (calls package APIs, no preset builder)
-└── flush.ts ← add multi-tenancy cache invalidation (calls package's flushTenantInstance)
-
-graphql/server/src/
-├── server.ts ← wire shutdownMultiTenancyCache, createFlushMiddleware
-└── index.ts ← export createFlushMiddleware
-
-graphql/env/src/
-└── env.ts ← add USE_MULTI_TENANCY_CACHE env var
-
-graphql/types/src/
-└── graphile.ts ← add useMultiTenancyCache to ApiOptions
-```
-
-### Benchmark scripts: `graphql/server/perf/`
-
-E2E benchmark scripts live at the server level (not in the package) since they
-start the actual GraphQL server, manage databases, and do HTTP load testing.
-
-```
-graphql/server/perf/
-├── README.md ← usage docs
-├── common.mjs ← shared utilities (fetch, timing, pool helpers)
-├── run-k-sweep.mjs ← orchestrator: run both modes, compare results
-├── run-test-spec.mjs ← single-mode runner (dedicated or multi-tenant)
-├── phase1-preflight.mjs ← pre-flight checks (DB connectivity, server health)
-├── phase1-tech-validate-dbpm.mjs ← validate DBPM tenant databases exist
-├── phase2-load.mjs ← HTTP load generator (configurable workers, duration)
-├── seed-real-multitenant.mjs ← seed k tenant databases for benchmarking
-├── build-token-pool.mjs ← generate auth tokens for load testing
-├── build-keyspace-profiles.mjs ← build tenant keyspace profiles
-├── build-business-op-profiles.mjs ← build business operation profiles
-├── prepare-public-test-access.mjs ← prepare public API test access
-├── public-test-access-lib.mjs ← shared lib for public test access
-├── reset-business-test-data.mjs ← reset test data between runs
-├── run-comparison.sh ← shell wrapper: run both modes + compare
-└── results/ ← raw JSON benchmark results (gitignored)
-```
-
----
-
-## Module specifications
-
-### 1. `pg-client-wrapper-plugin.ts`
-
-**Purpose:** Grafast middleware plugin that intercepts `client.query()` to transform SQL per-request.
-
-**Exports:**
-- `PgMultiTenancyWrapperPlugin: GraphileConfig.Plugin`
-
-**Internal functions:**
-- `createSqlTransformProxy(client, transform)` — Proxy wrapping `query()` and `withTransaction()`
-- `wrapWithPgClient(original, contextValue)` — lazy wrapper that reads `pgSqlTextTransform` at call time
-
-**Behavior:**
-1. Runs in `grafast.middleware.prepareArgs` (after `PgContextPlugin`)
-2. Iterates all `pgServices`, wraps each `withPgClient` function on `contextValue`
-3. At execution time, reads `contextValue.pgSqlTextTransform`
-4. If transform exists: proxy `client.query()` to transform `opts.text`
-5. If no transform: pass through unchanged
-6. Also wraps `client.withTransaction()` for transaction-scoped queries
-
-**Dependencies:** None (pure Grafast plugin, no external imports)
-
-### 2. `multi-tenancy-cache.ts`
-
-**Purpose:** Top-level orchestrator — owns the full tenant lifecycle including the `tenantInstances` Map.
-
-**Exports:**
-- `configureMultiTenancyCache({ basePresetBuilder })` — one-time package bootstrap; stores wrapped preset builder internally.
-- `getOrCreateTenantInstance(config)` → `Promise` — resolves tenant, stores in internal `tenantInstances` map. Uses the package-owned wrapped preset builder configured at bootstrap.
-- `getTenantInstance(cacheKey)` → `TenantInstance | undefined` — fast-path lookup from internal map
-- `flushTenantInstance(cacheKey)` — evict from `tenantInstances` map + deregister from template refCount
-- `getMultiTenancyCacheStats()` → `MultiTenancyCacheStats`
-- `shutdownMultiTenancyCache()` — release all resources (templates, dedicated instances, introspection cache, tenantInstances)
-- Types: `TenantConfig`, `TenantInstance`, `MultiTenancyCacheStats`
-
-**Internal state:**
-- `tenantInstances: Map` — fast-path cache of resolved tenant instances
-- `creatingTenants: Map>` — single-flight for tenant creation
-- `creatingTemplates: Map>` — single-flight for template creation
-- `dedicatedInstances: Map` — fallback non-shared instances, tracked with lifecycle metadata (createdAt, lastUsedAt, source=introspection-failure)
-
-**Flow (getOrCreateTenantInstance):**
-1. Check `tenantInstances` map (fast path) → return if hit
-2. Check `creatingTenants` map (single-flight coalesce) → wait if in-flight
-3. `getOrCreateIntrospection(pool, schemas, connectionKey)` → fingerprint
-4. `getTemplate(fingerprint)` → hit? → reuse, `registerTenant()`
-5. Miss → check `creatingTemplates` (single-flight for template)
-6. Miss → `createTemplate()` (builds PostGraphile instance, `setTemplate()`)
-7. Build `TenantInstance` with `buildSchemaRemapTransform()` as `sqlTextTransform`
-8. Store in `tenantInstances` map → return
-
-**Preset wrapping (package-owned):**
-`configureMultiTenancyCache()` wraps the base preset builder once to add:
-1. `plugins: [PgMultiTenancyWrapperPlugin]`
-2. `grafast.context` callback that reads `svc_key` from `requestContext.expressv4.req.svc_key`, looks up the tenant's `sqlTextTransform` from the internal `tenantInstances` map, and injects it as `pgSqlTextTransform` on the Grafast context — **no `req.sqlTextTransform` field needed on Express.Request**
-
-**Fallback:** If introspection fails, creates a dedicated (non-shared) instance (resilience over visibility).
-Fallback instances MUST be lifecycle-bound: cleaned by `flushTenantInstance()`, swept by idle TTL/LRU, and always released by `shutdownMultiTenancyCache()` so they cannot linger after cache flush/eviction.
-
-**Dependencies:** `introspection-cache`, `registry-template-map`, `utils/sql-transform`, `utils/schema-map`, `postgraphile`, `grafserv`, `express`
-
-### 3. `registry-template-map.ts`
-
-**Purpose:** Global template registry with lifecycle management.
-
-**Exports:**
-- `getTemplate(fingerprint)` → `RegistryTemplate | undefined`
-- `setTemplate(fingerprint, template)`
-- `registerTenant(cacheKey, fingerprint)` — increment refCount
-- `deregisterTenant(cacheKey)` — decrement refCount, mark idle
-- `sweepIdleTemplates()` — evict expired + over-cap templates
-- `clearAllTemplates()` — shutdown cleanup
-- `getTemplateStats()` — diagnostic stats
-- `_testSetMaxTemplates(n)` — test-only hook
-- Type: `RegistryTemplate`
-
-**Eviction policy:**
-- **TTL:** Templates with `refCount === 0` and `idleSince` older than 30min are evicted
-- **LRU cap:** When `templateMap.size > MAX_TEMPLATES` (50), oldest idle templates evicted first
-- **Periodic sweep:** Every 5min (lazy-started, `unref()`'d for clean exit)
-- **Active protection:** Templates with `refCount > 0` are never evicted
-- **Cleanup:** `disposeTemplate()` calls `pgl.release()` + `httpServer.close()`
-
-### 4. `introspection-cache.ts`
-
-**Purpose:** In-memory cache for parsed introspection results + fingerprints.
-
-**Exports:**
-- `getOrCreateIntrospection(pool, schemas, connectionKey)` → `Promise`
-- `invalidateIntrospection(connectionKey, schemas?)` — targeted invalidation
-- `clearIntrospectionCache()` — full clear + stop sweep timer
-- `sweepIntrospectionCache()` — evict expired + over-cap entries
-- `getIntrospectionCacheStats()` → `IntrospectionCacheStats`
-- `_testSetMaxEntries(n)` — test-only hook
-- Types: `CachedIntrospection`, `IntrospectionCacheStats`
-
-**Key:** `connHash:schema1,schema2` (schemas sorted alphabetically)
-`connHash` is derived from normalized connection identity:
-- `host`, `port`, `database`, `user` (and connection mode such as `sslmode`/socket when relevant)
-- Canonicalized + hashed to avoid leaking credentials while preventing cross-environment collisions.
-
-**Eviction policy:** Same pattern as template cache — TTL (30min idle) + LRU cap (100 entries) + periodic sweep (5min).
-
-**Single-flight:** `inflight` Map coalesces concurrent requests. `finally` block guarantees cleanup. Failed entries are NOT cached.
-
-### 5. `utils/fingerprint.ts`
-
-**Purpose:** Schema-name-agnostic structural fingerprinting.
-
-**Exports:**
-- `getSchemaFingerprint(introspection, schemaNames?)` → SHA-256 hex string
-- `fingerprintsMatch(a, b)` → boolean
-- Types: `MinimalIntrospection`, `IntrospectionClass`, `IntrospectionAttribute`, `IntrospectionConstraint`, `IntrospectionType`, `IntrospectionNamespace`, `IntrospectionProc`
-
-**What's included in fingerprint:** Table names, column names, data types, constraints, function signatures.
-
-**What's excluded:** Schema/namespace names, OIDs, instance-specific identifiers. This ensures `t_1_services_public.apis` and `t_2_services_public.apis` produce the same fingerprint.
-
-### 6. `utils/sql-transform.ts`
-
-**Purpose:** SQL remap engine with AST-safe rewrite and cache-backed fast path.
-
-**Exports:**
-- `buildSchemaRemapTransform(schemaMap)` → `(text: string) => string`
-
-**How it works:**
-1. Computes a cache key from `(sqlTextHash, schemaMapHash)`
-2. On cache hit: returns pre-rewritten SQL immediately (hot path)
-3. On cache miss: `parse -> rewrite semantic schema nodes -> deparse`
-4. Stores rewritten SQL in LRU/TTL cache for subsequent hits
-5. Empty schema map → identity function (no-op)
-
-### SQL Remap Safety Contract (v3)
-
-The SQL remap layer MUST follow these rules:
-
-1. **Semantic rewrite only**
-- Rewrite schema names via PostgreSQL AST node fields (e.g. relation namespace/schema), not global text substitution.
-- Do not rewrite literals, comments, dollar-quoted blocks, aliases, or unqualified identifiers.
-
-2. **Fail-closed by default**
-- If parse/rewrite/deparse fails, do not silently pass-through the original SQL.
-- Default behavior is request failure with structured error and telemetry.
-- Optional rollout mode may fallback to dedicated handler, but must be explicitly enabled and metered.
-
-3. **Cache-backed performance model**
-- Hot path is cache lookup only.
-- AST parse/rewrite/deparse occurs only on cache miss.
-- Cache policy: bounded LRU + TTL.
-
-4. **Observability requirements**
-- Emit counters/histograms for hit/miss, rewrite latency, rewrite failures, and fallback usage (if enabled).
-- Include tenant key and SQL hash in sampled debug logs.
-
-5. **Validation requirements**
-- Tests must prove that schema-qualified identifiers are remapped correctly.
-- Tests must prove literals/comments/dollar-quoted content are unchanged.
-- Transaction path (`withTransaction`) must apply identical remap behavior.
-
-### 7. `utils/schema-map.ts`
-
-**Purpose:** Schema mapping and pgSettings helpers.
-
-**Exports:**
-- `buildSchemaMap(templateSchemas, tenantSchemas)` → `Record`
-- `buildTenantPgSettings(tenantSchemas)` → `Record` (includes `search_path`)
-- `remapSchemas(templateSchemas, templatePrefix, tenantPrefix)` → `string[]`
-- Type: `SchemaMapping`
-
-### 8. `utils/introspection-query.ts`
-
-**Purpose:** Low-level introspection fetch + parse.
-
-**Exports:**
-- `fetchIntrospection(pool, schemas)` → raw JSON string
-- `parseIntrospection(text)` → `MinimalIntrospection`
-- `fetchAndParseIntrospection(pool, schemas)` → `{ raw, parsed }`
-
-**Connection safety:** Uses `BEGIN` + `SET LOCAL search_path` + `COMMIT` so the search_path never leaks to pooled connections.
-
----
-
-## Server integration
-
-The server is a thin consumer of the package APIs. It does **not** manage tenant
-state, preset wrapping logic, or Express.Request extensions — those responsibilities
-belong to the package.
-
-### `graphile.ts` changes
-
-**New function: `multiTenancyHandler(opts)`**
-- Selected when `opts.api.useMultiTenancyCache === true`
-- Calls `configureMultiTenancyCache({ basePresetBuilder })` once at startup (package owns wrapping)
-- Calls `getTenantInstance(key)` for fast-path cache hit
-- On miss, calls `getOrCreateTenantInstance(config)` — no preset builder passed from server
-- Routes the request to `tenant.handler(req, res, next)` — the package's Grafast context callback handles `pgSqlTextTransform` injection internally (no `req.sqlTextTransform` needed)
-
-**New exports:**
-- `isMultiTenancyCacheEnabled(opts)` — boolean check
-- `shutdownMultiTenancy()` — calls package's `shutdownMultiTenancyCache()`
-
-**No changes to `types.ts`** — `Express.Request` is NOT extended with `sqlTextTransform`. The transform is injected directly into the Grafast context by the package's preset builder using the existing `req.svc_key`.
-
-### `flush.ts` changes
-
-**New function: `createFlushMiddleware(opts)`**
-- Replaces `flush` (deprecated but kept for backwards compat)
-- Calls package's `flushTenantInstance(key)` + `invalidateIntrospection(connectionKey)`
-
-**`flushService()` changes:**
-- When multi-tenancy enabled: resolves canonical `connectionKey` from `databaseId`, calls `invalidateIntrospection(connectionKey)` + `flushTenantInstance(key)` for each matching domain
-- Flush path must also release any tenant-bound fallback dedicated instances immediately.
-
-### `env.ts` + `graphile.ts` (types) changes
-
-- Add `USE_MULTI_TENANCY_CACHE` env var → `api.useMultiTenancyCache: boolean`
-- Default: `false` (opt-in)
-
----
-
-## Activation
-
-```bash
-# Enable multi-tenancy cache
-USE_MULTI_TENANCY_CACHE=true
-
-# For old (dedicated) mode, enlarge cache to avoid eviction churn:
-# GRAPHILE_CACHE_MAX= where K = tenant count (min 100)
-```
-
-When `useMultiTenancyCache` is `false` (default), the server uses the existing `graphile-cache` (one PostGraphile instance per `svc_key`) — zero behavioral change.
-
----
-
-## Dependencies
-
-```json
-{
- "dependencies": {
- "@pgpmjs/logger": "workspace:^",
- "express": "^5.2.1",
- "grafserv": "1.0.0",
- "graphile-config": "1.0.0",
- "pg": "^8.11.3",
- "pg-env": "workspace:^",
- "pg-introspection": "1.0.0",
- "pgsql-deparser": "^17.18.2",
- "pgsql-parser": "^17.9.14",
- "postgraphile": "5.0.0"
- },
- "devDependencies": {
- "@types/express": "^5.0.6",
- "@types/pg": "^8.10.9",
- "makage": "^0.3.0",
- "ts-node": "^10.9.2"
- }
-}
-```
-
-No Crystal fork. No `link:` overrides. Works with published Crystal/PostGraphile packages.
-
----
-
-## Test plan
-
-### Unit tests (in `src/__tests__/`)
-
-| Test file | Coverage |
-|---|---|
-| `pg-client-wrapper-plugin.test.ts` | Proxy intercepts query/withTransaction, lazy transform read, no-op passthrough, release preservation |
-| `registry-template-map.test.ts` | register/deregister refCount, TTL eviction, LRU cap eviction, active-template protection, sweep timer, exact-cap boundary |
-| `introspection-cache.test.ts` | Cache hit/miss, single-flight coalescing, failure retry, TTL eviction, LRU cap eviction, invalidation |
-| `fingerprint.test.ts` | Same-structure-different-schema → same fingerprint, different-structure → different fingerprint, constraint normalization |
-| `sql-transform.test.ts` | AST schema-node rewrite, cache hit/miss path, identity transform, multi-schema remap, literal/comment non-rewrite |
-| `schema-map.test.ts` | Schema mapping, pgSettings generation, prefix remapping |
-| `single-flight.test.ts` | Concurrent creation coalescing, failure propagation |
-
-### E2E validation
-
-- Start server with `USE_MULTI_TENANCY_CACHE=true`
-- Send requests for k tenants with identical schemas
-- Assert: 0 errors, template count = 1, all tenants sharing
-- Compare QPS/latency/memory vs dedicated mode
-- Run a **fresh 3-minute benchmark** on the v3 no-Crystal path; do not reuse v2 data
-- Store raw benchmark JSON + environment metadata under `graphql/server/perf/results/`
-
----
-
-## Historical v2 baseline (reference only, k=20)
-
-| Metric | Dedicated (Old) | Multi-tenant (New) | Improvement |
-|---|---|---|---|
-| QPS | 706 | 780 | +10.5% |
-| p50 latency | 11ms | 11ms | same |
-| p99 latency | 42ms | 29ms | -31% |
-| Heap growth | +1,276 MB | +334 MB | 73.8% less |
-| RSS growth | +1,697 MB | +845 MB | 50.2% less |
-| PostGraphile builds | 20 | 0 | eliminated |
-| Cold start (2nd+) | 412ms | 7ms | 98.3% faster |
-
-This table is historical context from v2 only; it is **not** acceptance evidence for v3.
-
-## v3 performance acceptance (required, no-Crystal path)
-
-Acceptance MUST be based on fresh v3 benchmark runs using published Crystal/PostGraphile packages (no Crystal fork, no `link:` overrides):
-
-1. Benchmark duration: minimum **3 minutes** continuous load per mode (dedicated vs v3 multi-tenancy cache).
-2. Correctness gate: 0 request errors and expected tenant routing behavior.
-3. Performance gate: v3 multi-tenancy cache must show measurable improvement vs dedicated mode in at least one primary metric (QPS or p99 latency), without material regressions in stability.
-4. Resource gate: memory growth (heap/RSS) in v3 multi-tenancy cache must be no worse than dedicated mode under the same run conditions.
-5. Provenance gate: attach raw result files and run metadata (commit SHA, env flags, tenant count, concurrency, duration) in `graphql/server/perf/results/`.
-
-*Old mode given `GRAPHILE_CACHE_MAX=120` (best-case, zero eviction).*
-
----
-
-## Implementation order
-
-1. **Package scaffolding** — `package.json`, tsconfig, jest config
-2. **Utilities** — `utils/fingerprint.ts`, `utils/sql-transform.ts`, `utils/schema-map.ts`, `utils/introspection-query.ts`
-3. **Cache layers** — `introspection-cache.ts`, `registry-template-map.ts`
-4. **Plugin** — `pg-client-wrapper-plugin.ts`
-5. **Orchestrator** — `multi-tenancy-cache.ts`
-6. **Public API** — `index.ts`
-7. **Server integration** — `middleware/graphile.ts`, `flush.ts`, `env.ts`, `types/graphile.ts`, `server.ts`, `index.ts`
-8. **Tests** — unit tests for all modules
-9. **Benchmark scripts** — `graphql/server/perf/` (e2e load testing framework)
-10. **Validation** — e2e test run
From f654cd789f7f02e2455cc1cf117388f56ec5668f Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 21 Apr 2026 08:06:21 +0800
Subject: [PATCH 03/17] update docs and comments
---
.../graphile-multi-tenancy-cache/README.md | 227 ++++++++++++++--
.../graphile-multi-tenancy-cache/package.json | 8 +-
.../src/__tests__/buildkey.test.ts | 2 +-
.../graphile-multi-tenancy-cache/src/index.ts | 4 +-
.../src/multi-tenancy-cache.ts | 4 +-
graphql/server/perf/E2E_BENCHMARK_REPORT.md | 254 +++++++++---------
graphql/server/perf/README.md | 236 ++++++++++------
graphql/server/src/middleware/flush.ts | 2 +-
8 files changed, 495 insertions(+), 242 deletions(-)
diff --git a/graphile/graphile-multi-tenancy-cache/README.md b/graphile/graphile-multi-tenancy-cache/README.md
index 83b0a4a5ab..3654d53d95 100644
--- a/graphile/graphile-multi-tenancy-cache/README.md
+++ b/graphile/graphile-multi-tenancy-cache/README.md
@@ -1,24 +1,221 @@
# graphile-multi-tenancy-cache
-Template-based multi-tenancy optimization for PostGraphile v5. Shares PostGraphile instances across tenants with identical schema structures using AST-based SQL rewriting.
+
+
+
-## Features
+
+
+
+
+
+
+
+
+Multi-tenancy cache utilities for PostGraphile. This package combines an exact-match buildKey handler cache with introspection-cache helpers for Constructive's GraphQL server runtime.
+
+The current runtime model is intentionally conservative:
+
+- reuse handlers only when build inputs match exactly
+- no template sharing
+- no SQL rewrite
+- no fingerprint-based handler reuse in the request path
-- **Schema fingerprinting** — structural comparison (tables, columns, constraints) ignoring schema names
-- **Template sharing** — one PostGraphile instance per unique fingerprint, shared across N tenants
-- **AST-based SQL rewrite** — safe schema remapping via `pgsql-parser`/`pgsql-deparser`
-- **Three cache layers** — introspection cache, template registry, SQL rewrite cache (all LRU + TTL)
-- **No Crystal fork** — wrapper plugin intercepts at `withPgClient` level via Grafast middleware
+## Table of contents
-## Architecture
+- [Installation](#installation)
+- [Usage](#usage)
+- [Features](#features)
+- [Core concepts](#core-concepts)
+- [How the handler cache works](#how-the-handler-cache-works)
+- [Introspection cache](#introspection-cache)
+- [API](#api)
+- [License](#license)
+## Installation
+
+```bash
+npm install graphile-multi-tenancy-cache
```
-Request → authenticate → resolve tenant schemas
- → introspection cache (fingerprint lookup)
- → template registry (shared PostGraphile instance)
- → PgMultiTenancyWrapperPlugin (client.query Proxy)
- → AST rewrite cache (schema name remapping)
- → PostgreSQL
+
+## Usage
+
+This package is a runtime orchestrator, not a schema plugin. You configure it with a preset builder, then resolve handlers per request.
+
+```typescript
+import {
+ configureMultiTenancyCache,
+ getTenantInstance,
+ getOrCreateTenantInstance,
+ flushTenantInstance,
+ shutdownMultiTenancyCache,
+} from 'graphile-multi-tenancy-cache';
+
+configureMultiTenancyCache({
+ basePresetBuilder(pool, schemas, anonRole, roleName) {
+ return {
+ extends: [],
+ grafast: {
+ context: () => ({})
+ },
+ pgServices: [],
+ };
+ },
+});
+
+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,
+ });
+ }
+
+ tenant.handler(req, res);
+}
+
+process.on('SIGTERM', async () => {
+ await shutdownMultiTenancyCache();
+});
```
-See [SPEC.md](./SPEC.md) for full architecture documentation.
+## Features
+
+- **Exact-match buildKey reuse** — handlers are shared only when connection identity, schema set, and role inputs 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
+- **Introspection cache helpers** — separate connection-and-schema keyed cache for parsed introspection results
+- **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. 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`
+
+It does **not** include:
+
+- `svc_key`
+- `databaseId`
+- request host/domain
+- auth tokens or transient headers
+
+Schema order is preserved. `['a', 'b']` and `['b', 'a']` intentionally produce different buildKeys.
+
+## 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, and role inputs.
+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
+
+## Introspection cache
+
+The package also exports a separate introspection cache module.
+
+This cache is keyed by:
+
+- connection key
+- sorted schema list
+
+It stores:
+
+- raw introspection payload
+- parsed introspection structure
+- a fingerprint derived from the parsed result
+
+This is useful for:
+
+- diagnostics
+- perf tooling
+- supporting workflows that need repeated `pg_catalog` introspection
+
+Important: the current runtime does **not** rely on introspection fingerprints to decide handler reuse. Handler reuse is exact-match on build inputs only.
+
+## API
+
+### Handler cache orchestrator
+
+| Export | Purpose |
+|--------|---------|
+| `configureMultiTenancyCache(config)` | Registers the base preset builder. 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)` | Compute the exact-match handler identity. |
+| `getBuildKeyForSvcKey(svcKey)` | Resolve the buildKey currently mapped to a route key. |
+
+### Introspection cache
+
+| Export | Purpose |
+|--------|---------|
+| `getOrCreateIntrospection(pool, schemas, connectionKey)` | Get or build a cached introspection result. |
+| `invalidateIntrospection(connectionKey, schemas?)` | Invalidate one or all entries for a connection key. |
+| `clearIntrospectionCache()` | Clear the introspection cache completely. |
+| `getIntrospectionCacheStats()` | Return introspection cache stats. |
+| `getConnectionKey(pool)` | Derive the connection cache key for a pool. |
+
+### Lower-level utilities
+
+| Export | Purpose |
+|--------|---------|
+| `getSchemaFingerprint(...)` | Fingerprint helper over parsed introspection data. |
+| `fetchIntrospection(...)` | Fetch raw introspection from PostgreSQL. |
+| `parseIntrospection(...)` | Parse introspection payload into the normalized structure used by this package. |
+| `fetchAndParseIntrospection(...)` | Convenience helper for fetch + parse. |
+
+## License
+
+MIT
diff --git a/graphile/graphile-multi-tenancy-cache/package.json b/graphile/graphile-multi-tenancy-cache/package.json
index 68888ff254..224bdf1175 100644
--- a/graphile/graphile-multi-tenancy-cache/package.json
+++ b/graphile/graphile-multi-tenancy-cache/package.json
@@ -2,7 +2,7 @@
"name": "graphile-multi-tenancy-cache",
"version": "0.1.0",
"author": "Constructive ",
- "description": "Template-based multi-tenancy cache for PostGraphile v5 — shares instances across tenants with identical schemas",
+ "description": "Multi-tenancy cache utilities for PostGraphile — exact-match buildKey handler reuse with introspection-cache helpers",
"main": "index.js",
"module": "esm/index.js",
"types": "index.d.ts",
@@ -30,9 +30,9 @@
"graphile",
"multi-tenancy",
"cache",
- "template",
- "constructive",
- "v5"
+ "buildkey",
+ "introspection",
+ "constructive"
],
"bugs": {
"url": "https://github.com/constructive-io/constructive/issues"
diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
index f6d6195a6d..7e9977ab6f 100644
--- a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
@@ -1,5 +1,5 @@
/**
- * Unit tests for buildKey-based handler caching (v4-buildkey).
+ * Unit tests for buildKey-based handler caching.
*
* Tests cover:
* - buildKey computation determinism and sensitivity
diff --git a/graphile/graphile-multi-tenancy-cache/src/index.ts b/graphile/graphile-multi-tenancy-cache/src/index.ts
index 2fe40d1df7..9788d76c4f 100644
--- a/graphile/graphile-multi-tenancy-cache/src/index.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/index.ts
@@ -18,7 +18,7 @@ export type {
MultiTenancyCacheConfig,
} from './multi-tenancy-cache';
-// --- Introspection cache (kept as module/test base — not required for v4 runtime) ---
+// --- Introspection cache (kept as module/test base — not required for the handler runtime) ---
export {
getOrCreateIntrospection,
invalidateIntrospection,
@@ -32,7 +32,7 @@ export type {
IntrospectionCacheStats,
} from './introspection-cache';
-// --- Utilities (kept as module base — not required for v4 runtime) ---
+// --- Utilities (kept as module base — not required for the handler runtime) ---
export { getSchemaFingerprint } from './utils/fingerprint';
export type { MinimalIntrospection } from './utils/fingerprint';
export { fetchIntrospection, parseIntrospection, fetchAndParseIntrospection } from './utils/introspection-query';
diff --git a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
index 157aef2d72..944cb38324 100644
--- a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
@@ -1,5 +1,5 @@
/**
- * Multi-tenancy cache orchestrator (v4-buildkey).
+ * Multi-tenancy cache orchestrator.
*
* Caches one independent PostGraphile handler per **buildKey** (derived from
* the inputs that materially affect Graphile handler construction).
@@ -105,7 +105,7 @@ export interface MultiTenancyCacheConfig {
*/
export function configureMultiTenancyCache(config: MultiTenancyCacheConfig): void {
presetBuilder = config.basePresetBuilder;
- log.info('Multi-tenancy cache configured (v4-buildkey — buildKey-based handler caching)');
+ log.info('Multi-tenancy cache configured (buildKey-based handler caching)');
}
// --- BuildKey computation ---
diff --git a/graphql/server/perf/E2E_BENCHMARK_REPORT.md b/graphql/server/perf/E2E_BENCHMARK_REPORT.md
index dd07d50bea..bfb945f530 100644
--- a/graphql/server/perf/E2E_BENCHMARK_REPORT.md
+++ b/graphql/server/perf/E2E_BENCHMARK_REPORT.md
@@ -1,155 +1,151 @@
-# E2E Multi-Tenancy Cache Benchmark Report
+# Multi-Tenancy Cache Stress Test Report
+
+This document captures the stress-test results for the current multi-tenancy caching strategy.
+
+The optimization logic evaluated here is:
+
+- introspection caching
+- exact-match buildKey handler reuse
+- perf stress testing of the old vs new request path
## Test Configuration
| Parameter | Value |
|---|---|
-| **Mode** | `apiIsPublic=false` (header-based routing via `X-Schemata` + `X-Database-Id`) |
-| **Tenants (k)** | 20 |
-| **Duration** | 5 minutes (300s) per mode |
-| **Workers** | 8 concurrent |
-| **Schema** | `services_public` |
-| **Server** | Constructive GraphQL server (PR #3 branch, linked Crystal PR #5) |
-| **Database** | PostgreSQL 18 via pgpm (`constructive-local` deployed) |
+| **Server** | Constructive GraphQL server |
+| **Database** | PostgreSQL on localhost:5432, `postgres_perf` |
| **Node.js** | `NODE_ENV=development` |
-| **Old approach GRAPHILE_CACHE_MAX** | 120 (enlarged to prevent cache eviction churn — gives old approach best-case performance) |
-
-## Query Mix
-
-| Operation | Weight | Description |
-|---|---|---|
-| `ListApis` | 40% | `{ apis(first: 10) { nodes { id name dbname isPublic } totalCount } }` |
-| `ListApps` | 20% | `{ apps(first: 10) { nodes { id name databaseId } totalCount } }` |
-| `ListDomains` | 20% | `{ domains(first: 10) { nodes { id domain subdomain } totalCount } }` |
-| `Introspection` | 10% | `{ __schema { queryType { name } mutationType { name } types { name kind } } }` |
-| `MetaQuery` | 10% | `{ _meta { tables { name schemaName fields { name } } } }` |
-
-All queries are real GraphQL HTTP requests through the full Express → PostGraphile → Grafast pipeline.
-
-## Throughput & Latency
-
-| Metric | Dedicated (Old, CACHE_MAX=120) | Multi-tenant (New) | Delta |
-|---|---|---|---|
-| **Total Queries** | 212,052 | 234,125 | **+10.4%** |
-| **Errors** | 0 | 0 | — |
-| **QPS** | 706 | 780 | **+10.5%** |
-| **p50 Latency** | 11ms | 11ms | same |
-| **p95 Latency** | 23ms | 16ms | **-30.4% (7ms faster)** |
-| **p99 Latency** | 42ms | 29ms | **-31.0% (13ms faster)** |
-
-> **Note:** The old approach was given `GRAPHILE_CACHE_MAX=120` (6× the number of tenants) to eliminate
-> cache eviction churn entirely. This represents the **best-case scenario** for the dedicated-instance
-> approach. Even with this advantage, the multi-tenancy cache still outperforms it across every metric.
-
-## Server-Side Memory
-
-Both snapshots captured from the server's `/debug/memory` endpoint before and after the 5-minute sustained load.
-
-| Metric | Dedicated (Old, CACHE_MAX=120) | Multi-tenant (New) | Delta |
-|---|---|---|---|
-| **Heap Start** | 321.8 MB | 321.8 MB | same |
-| **Heap End** | 1,597.8 MB | 656.1 MB | **-941.7 MB (-58.9%)** |
-| **Heap Growth** | +1,276.0 MB | +334.3 MB | **-941.7 MB (73.8% less growth)** |
-| **RSS Start** | 421.2 MB | 421.2 MB | same |
-| **RSS End** | 2,118.4 MB | 1,265.9 MB | **-852.5 MB (-40.2%)** |
-| **RSS Growth** | +1,697.2 MB | +844.8 MB | **-852.4 MB (50.2% less growth)** |
-| **External (end)** | 173.7 MB | 159.7 MB | -14.1 MB (-8.1%) |
-
-> **RSS** (Resident Set Size) is the total physical RAM the server process occupies, as reported by the OS.
-> It includes heap, external buffers, code segments, stacks, and shared libraries. RSS is what matters for
-> production capacity planning — it's the real memory footprint.
-
-### Cache Internals
-
-| Metric | Dedicated (Old) | Multi-tenant (New) |
-|---|---|---|
-| Graphile Cache entries | 20/120 (all tenants cached) | 0/15 (bypassed entirely) |
-| Svc Cache entries | 20/25 | 20/25 |
-| PostGraphile Builds (started) | 20 | **0** (template reuse) |
-| PostGraphile Builds (succeeded) | 20 | **0** |
-| Build Time (total) | 112ms | **0ms** |
-
-> Even with `GRAPHILE_CACHE_MAX=120` (no eviction churn), the old approach still grows **1.3 GB heap**
-> and **1.7 GB RSS** over 5 minutes — each of the 20 dedicated PostGraphile instances maintains its own
-> operation plan cache, compiled schema, and V8 closures. The new approach grows **3.8× less heap** and
-> **2× less RSS** because all 20 tenants share a single compiled template with one operation plan cache.
-
-## Cold Start (per-tenant schema build)
-
-| Metric | Dedicated (Old) | Multi-tenant (New) | Delta |
-|---|---|---|---|
-| 1st tenant | 873ms | 1,372ms | +57% (full template build) |
-| 2nd+ tenant avg | 412ms | **7ms** | **98.3% faster** |
-| Last tenant | 489ms | 5ms | **-99%** |
+| **Mode comparison** | OLD = dedicated instances (`GRAPHILE_CACHE_MAX` tuned); NEW = `USE_MULTI_TENANCY_CACHE=true` |
+| **Benchmark tool** | `e2e-benchmark.ts` through Express -> PostGraphile -> Grafast -> PostgreSQL |
+| **Query mix** | ListApis (40%), ListApps (20%), ListDomains (20%), Introspection (10%), MetaQuery (10%) |
+
+## Stress Matrix
+
+| # | Test | K | Workers | Duration | Schema Variants | Extras |
+|---|------|---|---------|----------|-----------------|--------|
+| 1 | High-K scale | 100 | 10 | 5 min | `SCHEMA_VARIANTS=4` | `MULTI_ENDPOINT=true` |
+| 2 | High concurrency | 30 | 10 | 5 min | `SCHEMA_VARIANTS=4` | `MULTI_ENDPOINT=true` |
+| 3 | Flush under load | 30 | 10 | 5 min | `SCHEMA_VARIANTS=4` | `CHAOS_FLUSH=true FLUSH_INTERVAL=30` |
+| 4 | Mixed buildKeys (max divergence) | 30 | 10 | 5 min | `SCHEMA_VARIANTS=8` | — |
+| 5 | Soak | 30 | 10 | 2 hr | `SCHEMA_VARIANTS=4` | `CHAOS_FLUSH=true FLUSH_INTERVAL=60` |
+| 6 | Startup burst | 30 | 10 | 1 min | `SCHEMA_VARIANTS=4` | `BURST_START=true` |
+
+Each test was run in OLD mode and NEW mode.
+
+## Results Summary
+
+### Throughput and Memory
+
+| # | Test | OLD QPS | NEW QPS | QPS Delta | OLD Heap Delta | NEW Heap Delta | Heap Saved | svc_keys | Errors |
+|---|------|---------|---------|-----------|----------------|----------------|------------|----------|--------|
+| 1 | High-K | 697 | 772 | +11% | 1,502 MB | 191 MB | -87% | 400 | 0 |
+| 2 | High concurrency | 717 | 731 | +2% | 648 MB | 262 MB | -60% | 120 | 0 |
+| 3 | Flush under load | 689 | 738 | +7% | 1,019 MB | 220 MB | -78% | 120 | 0 |
+| 4 | Mixed buildKeys | 730 | 760 | +4% | 950 MB | 51 MB | -95% | 240 | 0 |
+| 5 | Soak | 733 | 763 | +4% | 856 MB | 560 MB | -35% | 120 | 0 |
+| 6 | Startup burst | 728 | 746 | +2% | 1,216 MB | 264 MB | -78% | 120 | 0 |
+
+### Latency
+
+| # | Test | OLD p50 | NEW p50 | OLD p95 | NEW p95 | OLD p99 | NEW p99 |
+|---|------|---------|---------|---------|---------|---------|---------|
+| 1 | High-K | 14 ms | 14 ms | 21 ms | 19 ms | 25 ms | 21 ms |
+| 2 | High concurrency | 15 ms | 15 ms | 21 ms | 21 ms | 25 ms | 24 ms |
+| 3 | Flush under load | 15 ms | 14 ms | 23 ms | 21 ms | 30 ms | 25 ms |
+| 4 | Mixed buildKeys | 14 ms | 14 ms | 21 ms | 20 ms | 25 ms | 23 ms |
+| 5 | Soak | 15 ms | 14 ms | 21 ms | 20 ms | 24 ms | 23 ms |
+| 6 | Startup burst | 14 ms | 14 ms | 21 ms | 20 ms | 25 ms | 25 ms |
+
+### Soak Detail
+
+| Metric | OLD | NEW |
+|--------|-----|-----|
+| Total queries | 5,281,481 | 5,493,698 |
+| QPS | 733 | 763 |
+| Errors | 0 | 0 |
+| Chaos flush events | 119 | 119 |
+| Flush errors | 0 | 0 |
+| Avg flush latency | 23 ms | 23 ms |
## Analysis
-### Why GRAPHILE_CACHE_MAX matters for the old approach
+### Why the heap savings are so large
+
+The dominant effect is exact-match buildKey deduplication combined with cached introspection state.
+
+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 single handler.
+
+Those build inputs are:
+
+- connection identity
+- schema set
+- `anonRole`
+- `roleName`
+
+This means the memory benefit depends on the ratio between:
+
+- total `svc_key`s observed by the benchmark
+- distinct buildKeys that actually need handlers
+
+When many keys collapse onto a small set of buildKeys, heap savings become dramatic.
+
+### Why throughput gains are smaller than heap gains
+
+The benchmark exercises full HTTP traffic through Express, PostGraphile, Grafast, and PostgreSQL. That means request execution is still dominated by network and query work, not only handler reuse.
+
+The new path still wins because:
+
+- fewer handlers means less GC pressure
+- repeated introspection/bootstrap work is reduced
+- working-set size is smaller under load
+- hot handlers stay reused across many route keys
-Without enlarging `GRAPHILE_CACHE_MAX`, the legacy graphile-cache uses an LRU with `max=15` entries by default. With 20 tenants, the cache constantly evicts and rebuilds PostGraphile schema instances, causing:
+At higher `K` and higher `svc_key` fanout, these effects become more visible.
-- **1,110+ schema builds** triggered by eviction churn over 5 minutes
-- QPS drops to **~13** because requests are blocked on schema rebuilds
-- Heap peaks at **1.8 GB** and never reclaims
-- p50 latency rises to **212ms**, p99 to **2,559ms**
+### Why the soak run saves less heap than the short runs
-By setting `GRAPHILE_CACHE_MAX=120`, all 20 tenants fit in cache with room to spare. This eliminates churn and gives the old approach its best-case performance (~706 QPS, 11ms p50).
+The 2-hour soak includes repeated flushes. That means both modes spend time destroying and rebuilding runtime state instead of converging to a steady-state heap profile.
-### Why the new approach still wins
+The new mode still uses less memory, but the gap narrows because:
-Even when the old approach runs at its best (no eviction churn), the multi-tenancy cache delivers:
+- handlers are repeatedly evicted
+- rebuilt handlers temporarily increase active memory
+- steady-state reuse is interrupted by churn
-- **+10.5% higher QPS** (780 vs 706) — shared template means less per-request overhead
-- **30% lower tail latency** (p95: 16ms vs 23ms, p99: 29ms vs 42ms) — no per-tenant instance lookup variance
-- **98.3% faster tenant onboarding** (7ms vs 412ms avg cold start for 2nd+ tenants) — template reuse vs full schema build
-- **Zero PostGraphile builds during sustained load** — all operation plans compiled once and shared
+### Stability under concurrency and churn
-### Scaling implications
+The stress runs validate the runtime fixes added around the buildKey path:
-At production scale (k=100+), the old approach would need `GRAPHILE_CACHE_MAX=600+` to avoid churn, consuming proportionally more memory for 100 separate PostGraphile instances. The new approach scales with O(1) templates regardless of tenant count — memory grows only for the lightweight `svcCache` entries (~few KB each).
+- deferred registration prevents failed in-flight creation from leaving orphaned mappings
+- rebinding cleanup prevents stale buildKey entries from becoming unreachable leaks
+- the `svc_key` epoch guard prevents stale completions from overwriting newer bindings
-## How to Reproduce
+The 2-hour soak is the strongest signal here: millions of queries, repeated flushes, and no observed request errors.
-```bash
-# Start PostgreSQL
-pgpm docker start --image docker.io/constructiveio/postgres-plus:18
-eval "$(pgpm env)"
-pgpm deploy --yes --package constructive-local --recursive
+## What Drives the Results
-# Run the orchestrated benchmark (starts both modes automatically)
-cd graphql/server
-bash run-e2e-benchmark.sh 20 300 8
-# Args: K=20 tenants, DURATION=300s, WORKERS=8
+| Factor | Heap Impact | QPS Impact | Stability Impact |
+|--------|-------------|------------|------------------|
+| buildKey deduplication | dominant | moderate | — |
+| introspection caching | secondary | secondary | — |
+| reduced GC pressure | secondary | primary at higher fanout | — |
+| deferred registration | leak prevention | — | critical |
+| rebinding cleanup | leak prevention | — | critical |
+| epoch guard | — | — | critical |
-# Or run individual modes manually:
-# OLD mode (with enlarged cache):
-GRAPHILE_CACHE_MAX=120 MODE=old K=20 DURATION=300 WORKERS=8 npx ts-node e2e-benchmark.ts
+## Conclusion
-# NEW mode (start server with USE_MULTI_TENANCY_CACHE=true):
-MODE=new K=20 DURATION=300 WORKERS=8 npx ts-node e2e-benchmark.ts
-```
+The current strategy of introspection caching plus exact-match buildKey handler reuse delivers substantial memory savings while preserving stable throughput and correctness under load.
-## Files
+For this workload pattern, the results show:
-- `graphql/server/e2e-benchmark.ts` — Benchmark script (real GraphQL HTTP requests)
-- `graphql/server/run-e2e-benchmark.sh` — Orchestrator (runs both modes, compares results)
-- `graphql/server/perf/E2E_BENCHMARK_REPORT.md` — This report
-- `graphql/server/perf/results/e2e-benchmark-old-k20.json` — Raw OLD mode results
-- `graphql/server/perf/results/e2e-benchmark-new-k20.json` — Raw NEW mode results
+- large heap savings when many `svc_key`s collapse onto few buildKeys
+- modest but consistent QPS gains
+- stable long-running behavior under repeated flush churn
+- no need to reintroduce template sharing or SQL rewriting to get meaningful wins from handler reuse
-### Migrated perf framework scripts (from attachment)
+## Notes
-- `graphql/server/perf/common.mjs` — Core utilities (HTTP helpers, CLI arg parsing, file I/O)
-- `graphql/server/perf/phase1-preflight.mjs` — Preflight validation (server health, token pool, keyspace)
-- `graphql/server/perf/phase2-load.mjs` — Sustained load generation with cache activity tracking
-- `graphql/server/perf/run-k-sweep.mjs` — K-value sweep orchestrator
-- `graphql/server/perf/run-comparison.sh` — Bash orchestrator for old vs new comparison
-- `graphql/server/perf/run-test-spec.mjs` — Phase orchestrator (spawns phase1 + phase2)
-- `graphql/server/perf/build-token-pool.mjs` — Token pool builder
-- `graphql/server/perf/build-keyspace-profiles.mjs` — Keyspace expansion
-- `graphql/server/perf/build-business-op-profiles.mjs` — Business operation profiles
-- `graphql/server/perf/seed-real-multitenant.mjs` — Real tenant provisioning
-- `graphql/server/perf/reset-business-test-data.mjs` — Test data reset
-- `graphql/server/perf/prepare-public-test-access.mjs` — Public access setup
-- `graphql/server/perf/public-test-access-lib.mjs` — RLS policy helpers
-- `graphql/server/perf/README.md` — Perf framework documentation
+- This file is a results document for the current evaluation cycle.
+- For current script entrypoints and workflow guidance, use `README.md` in the same directory.
diff --git a/graphql/server/perf/README.md b/graphql/server/perf/README.md
index 61407e8d59..0d90bd34da 100644
--- a/graphql/server/perf/README.md
+++ b/graphql/server/perf/README.md
@@ -1,125 +1,185 @@
-# Constructive GraphQL Server — Performance Test Suite
+# Constructive GraphQL Server Perf Suite
-Migrated from the standalone perf framework into the Constructive monorepo.
-Runs real GraphQL HTTP requests through the full Express → PostGraphile → Grafast pipeline.
+This directory contains the performance tooling for the GraphQL server.
+
+The current optimization model being exercised is:
+
+- introspection caching
+- exact-match buildKey handler reuse
+- no template sharing
+- no SQL rewrite
+- no fingerprint-based handler reuse in the runtime path
+
+In practice, handlers are reused only when the build inputs match exactly:
+
+- connection identity
+- schema set
+- role configuration
+
+## Perf Lanes
+
+There are two distinct lanes in this folder.
+
+### 1. Lightweight HTTP comparison
+
+This lane drives real HTTP requests through Express -> PostGraphile -> Grafast -> PostgreSQL.
+
+Primary entrypoints:
+
+- `e2e-benchmark.ts`
+- `run-comparison.sh`
+- `run-stress-suite.sh`
+
+Use this lane when you want:
+
+- old vs new mode comparison
+- QPS / latency / heap snapshots
+- repeated server restarts and cache warm/cold behavior
+
+`e2e-benchmark.ts` is the simple benchmark entrypoint. It currently exercises the private-routing path with header-based requests and a fixed query mix.
+
+### 2. DBPM-backed perf framework
+
+This lane provisions real tenant data, builds token pools and request profiles, and can be used for longer-running or more realistic tenant workflows.
+
+Primary entrypoints:
+
+- `phase1-preflight.mjs`
+- `phase1-tech-validate-dbpm.mjs`
+- `phase2-load.mjs`
+- `run-test-spec.mjs`
+- `run-k-sweep.mjs`
+
+Use this lane when you want:
+
+- DBPM tenant provisioning
+- business-table validation
+- shape-variant experiments
+- profile-driven sustained load
+
+## Option A Shape Variants
+
+The DBPM flow now supports Option A shape divergence through additional provisioned tables created via the same provisioning mutation used for the main business table.
+
+Relevant files:
+
+- `phase1-tech-validate-dbpm.mjs`
+- `phase1-preflight.mjs`
+- `summarize-shapes.mjs`
+
+The intended usage is:
+
+1. Provision the normal tenant database and main business table.
+2. Add extra provisioned variant tables for selected tenants with `--dbpm-shape-variants`.
+3. Keep the main CRUD workload pointed only at the original business table.
+4. Use `summarize-shapes.mjs` to inspect structural divergence without relying on raw GraphQL introspection names.
## Quick Start
-### E2E Comparison (Old vs New)
+### Old vs New comparison
```bash
-# 5-minute debug run, k=20 tenants, 8 workers
-# Automatically enlarges GRAPHILE_CACHE_MAX for old approach
bash graphql/server/perf/run-comparison.sh --k 20 --duration 300 --workers 8
```
-### Individual Scripts
+This runs:
-```bash
-# Phase 1: Preflight checks
-node graphql/server/perf/phase1-preflight.mjs --run-dir /tmp/constructive-perf/run1
+- old mode with enlarged `GRAPHILE_CACHE_MAX`
+- new mode with `USE_MULTI_TENANCY_CACHE=true`
+- a simple side-by-side comparison of throughput, latency, and heap
-# Phase 2: Sustained load
-node graphql/server/perf/phase2-load.mjs --run-dir /tmp/constructive-perf/run1 \
- --workers 8 --duration-seconds 300
+### Stress suite
-# K-sweep: Multiple k-values with server restart per tier
-node graphql/server/perf/run-k-sweep.mjs --k-values 10,20 \
- --duration-seconds 300 --workers 8 --api-is-public false
+```bash
+bash graphql/server/perf/run-stress-suite.sh
+```
-# Seed real tenants
-node graphql/server/perf/seed-real-multitenant.mjs --tenant-count 20
+This is the curated multi-run shell harness for repeated old/new comparisons under a fixed matrix of scenarios.
-# Build token pool from credentials
-node graphql/server/perf/build-token-pool.mjs --run-dir /tmp/constructive-perf/run1
+### DBPM preflight + load
-# Build business operation profiles
-node graphql/server/perf/build-business-op-profiles.mjs --run-dir /tmp/constructive-perf/run1
+```bash
+node graphql/server/perf/phase1-preflight.mjs \
+ --run-dir /tmp/constructive-perf/run1 \
+ --dbpm-tenant-count 20 \
+ --dbpm-shape-variants 3
+
+node graphql/server/perf/phase2-load.mjs \
+ --run-dir /tmp/constructive-perf/run1 \
+ --workers 8 \
+ --duration-seconds 300
+```
-# Reset test data
-node graphql/server/perf/reset-business-test-data.mjs --run-dir /tmp/constructive-perf/run1
+### Shape summary
+
+```bash
+node graphql/server/perf/summarize-shapes.mjs \
+ --manifest /tmp/constructive-perf/run1/data/business-table-manifest.json
```
-## Architecture
+## Key Scripts
-### Phases
+| Script | Purpose |
+|---|---|
+| `e2e-benchmark.ts` | Simple HTTP benchmark through the full GraphQL stack |
+| `run-comparison.sh` | Old vs new comparison harness |
+| `run-stress-suite.sh` | Curated multi-run stress matrix |
+| `phase1-preflight.mjs` | Preflight orchestration, DBPM validation, token/keyspace/profile setup |
+| `phase1-tech-validate-dbpm.mjs` | DBPM tenant provisioning and business-table validation |
+| `phase2-load.mjs` | Sustained profile-driven GraphQL load |
+| `run-test-spec.mjs` | Wrapper around phase orchestration |
+| `run-k-sweep.mjs` | Multi-k orchestration |
+| `build-token-pool.mjs` | Sign-in and bearer-token generation |
+| `build-keyspace-profiles.mjs` | Route/keyspace expansion |
+| `build-business-op-profiles.mjs` | Business workload profile construction |
+| `reset-business-test-data.mjs` | Cleanup between runs |
+| `prepare-public-test-access.mjs` | Public-lane preparation |
+| `public-test-access-lib.mjs` | Shared helpers for public-lane access |
+| `summarize-shapes.mjs` | Name-agnostic structural shape summary |
-| Phase | Script | Purpose |
-|-------|--------|---------|
-| Preflight | `phase1-preflight.mjs` | Health checks, token pool building, keyspace expansion |
-| Tech Validate | `phase1-tech-validate-dbpm.mjs` | DBPM tenant provisioning, schema creation validation |
-| Load | `phase2-load.mjs` | Concurrent GraphQL workers, cache activity tracking, fail-fast guards |
-| K-Sweep | `run-k-sweep.mjs` | Multi-k orchestration with server restart per tier |
-| Comparison | `run-comparison.sh` | Old vs New side-by-side with enlarged `GRAPHILE_CACHE_MAX` for old mode |
+## Important Notes
-### Supporting Scripts
+### `GRAPHILE_CACHE_MAX`
-| Script | Purpose |
-|--------|---------|
-| `common.mjs` | Shared utilities (HTTP helpers, CLI arg parsing, file I/O) |
-| `seed-real-multitenant.mjs` | Creates real tenants (orgs, users, memberships) |
-| `build-token-pool.mjs` | Authenticates credentials, generates bearer tokens |
-| `build-keyspace-profiles.mjs` | Expands route keyspace via schema combinations |
-| `build-business-op-profiles.mjs` | Builds business operation profiles from manifest |
-| `reset-business-test-data.mjs` | Truncates business test tables between runs |
-| `prepare-public-test-access.mjs` | Sets up public lane access (grants, RLS policies) |
-| `public-test-access-lib.mjs` | Shared library for public access preparation |
-| `run-test-spec.mjs` | Phase orchestrator wrapper |
+For old-mode comparisons, enlarge `GRAPHILE_CACHE_MAX` so dedicated-instance mode is not artificially dominated by LRU churn.
-## Key Configuration
+`run-comparison.sh` already does this automatically.
-### GRAPHILE_CACHE_MAX
+### Interpreting the two lanes
-**CRITICAL**: When testing the old (dedicated) approach, always enlarge `GRAPHILE_CACHE_MAX`
-to prevent cache eviction churn that would artificially penalize the old strategy.
+The lightweight benchmark lane is best for:
-The `run-comparison.sh` script automatically sets this to `max(100, k * 6)`.
+- cache-mode comparison
+- heap growth
+- restart / warmup / burst behavior
-For manual runs:
-```bash
-# Old mode: enlarge cache to hold all tenant instances
-export GRAPHILE_CACHE_MAX=120 # for k=20 tenants × ~3 endpoints
+The DBPM-backed lane is best for:
-# New mode: no need to set (multi-tenancy cache bypasses the graphile LRU cache)
-```
+- tenant provisioning realism
+- shape-variant experiments
+- profile-driven workload behavior
-### Environment Variables
+### Report file
-| Variable | Default | Description |
-|----------|---------|-------------|
-| `PGHOST` | `localhost` | PostgreSQL host |
-| `PGPORT` | `5432` | PostgreSQL port |
-| `PGUSER` | `postgres` | PostgreSQL user |
-| `PGPASSWORD` | `password` | PostgreSQL password |
-| `PGDATABASE` | `postgres` | PostgreSQL database |
-| `API_IS_PUBLIC` | `false` | Routing mode (false = header-based private routing) |
-| `USE_MULTI_TENANCY_CACHE` | unset | Enable multi-tenancy cache (new approach) |
-| `GRAPHILE_CACHE_MAX` | `15` | Max graphile cache entries (enlarge for old approach!) |
+`E2E_BENCHMARK_REPORT.md` is the archived stress-test writeup for the current multi-tenancy cache evaluation. Treat it as a results document, not as the single source of truth for script capabilities.
-## Output
+## Output Layout
-All results are written to the run directory (`/tmp/constructive-perf//`):
+Most scripts write to a run directory under `/tmp/constructive-perf/`:
-```
+```text
/
-├── data/ # Profiles, tokens, manifests
-├── logs/ # Server logs, sampler output
-│ ├── sampler/ # Debug sampler JSONL files
-│ └── heap/ # Heap snapshots
-├── reports/ # Summary reports, analysis output
-└── tmp-scripts/ # Temporary script artifacts
+├── data/
+├── logs/
+├── reports/
+└── tmp-scripts/
```
-## Adaptations from Original Framework
-
-- `DEFAULT_TMP_ROOT` → `/tmp/constructive-perf` (was macOS user path)
-- Server start via `npx ts-node src/run.ts` (was `packages/cli/dist/index.js`)
-- Script paths adjusted for `graphql/server/perf/` location
-- `capture-heap-snapshot.mjs` and `analyze-debug-logs.mjs` referenced from `../scripts/`
-
-## TODO
+Typical artifacts include:
-Once Crystal PR #5 is published:
-- Remove `link:` overrides from package.json
-- Replace compat shim with direct import from `@dataplan/pg`
-- Re-run full k-sweep comparison with published packages
+- tenant credentials
+- token pools
+- business table manifests
+- business operation profiles
+- load summaries
+- debug and memory snapshots
diff --git a/graphql/server/src/middleware/flush.ts b/graphql/server/src/middleware/flush.ts
index d5716ce104..77aa5fba36 100644
--- a/graphql/server/src/middleware/flush.ts
+++ b/graphql/server/src/middleware/flush.ts
@@ -76,7 +76,7 @@ export const flushService = async (
});
}
- // v4-buildkey: flush all handlers associated with this databaseId via index
+ // Flush all handlers associated with this databaseId via index
if (multiTenancyEnabled) {
flushByDatabaseId(databaseId);
}
From 296e77da349439a5945a766a08500c08f5da9d96 Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 21 Apr 2026 08:44:39 +0800
Subject: [PATCH 04/17] clean outdated methods
---
.../graphile-multi-tenancy-cache/README.md | 52 +---
.../graphile-multi-tenancy-cache/package.json | 7 +-
.../src/__tests__/fingerprint.test.ts | 125 ----------
.../src/__tests__/introspection-cache.test.ts | 87 -------
.../graphile-multi-tenancy-cache/src/index.ts | 19 --
.../src/introspection-cache.ts | 232 ------------------
.../src/utils/fingerprint.ts | 175 -------------
.../src/utils/introspection-query.ts | 124 ----------
graphql/server/perf/E2E_BENCHMARK_REPORT.md | 7 +-
graphql/server/perf/README.md | 3 +-
graphql/server/perf/phase2-load.mjs | 2 +
pnpm-lock.yaml | 12 -
12 files changed, 10 insertions(+), 835 deletions(-)
delete mode 100644 graphile/graphile-multi-tenancy-cache/src/__tests__/fingerprint.test.ts
delete mode 100644 graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts
delete mode 100644 graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts
delete mode 100644 graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts
delete mode 100644 graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts
diff --git a/graphile/graphile-multi-tenancy-cache/README.md b/graphile/graphile-multi-tenancy-cache/README.md
index 3654d53d95..c771c23006 100644
--- a/graphile/graphile-multi-tenancy-cache/README.md
+++ b/graphile/graphile-multi-tenancy-cache/README.md
@@ -12,14 +12,14 @@
-Multi-tenancy cache utilities for PostGraphile. This package combines an exact-match buildKey handler cache with introspection-cache helpers for Constructive's GraphQL server runtime.
+Multi-tenancy cache utilities for PostGraphile. This package implements exact-match buildKey-based handler reuse for Constructive's GraphQL server runtime.
-The current runtime model is intentionally conservative:
+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 in the request path
+- no fingerprint-based handler reuse
## Table of contents
@@ -28,7 +28,6 @@ The current runtime model is intentionally conservative:
- [Features](#features)
- [Core concepts](#core-concepts)
- [How the handler cache works](#how-the-handler-cache-works)
-- [Introspection cache](#introspection-cache)
- [API](#api)
- [License](#license)
@@ -94,7 +93,6 @@ process.on('SIGTERM', async () => {
- **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
-- **Introspection cache helpers** — separate connection-and-schema keyed cache for parsed introspection results
- **Diagnostics-friendly** — exposes cache stats and `svc_key -> buildKey` lookup helpers
## Core concepts
@@ -158,33 +156,8 @@ The package supports:
- flushing all handlers associated with a `databaseId`
- full shutdown and disposal of cached handlers
-## Introspection cache
-
-The package also exports a separate introspection cache module.
-
-This cache is keyed by:
-
-- connection key
-- sorted schema list
-
-It stores:
-
-- raw introspection payload
-- parsed introspection structure
-- a fingerprint derived from the parsed result
-
-This is useful for:
-
-- diagnostics
-- perf tooling
-- supporting workflows that need repeated `pg_catalog` introspection
-
-Important: the current runtime does **not** rely on introspection fingerprints to decide handler reuse. Handler reuse is exact-match on build inputs only.
-
## API
-### Handler cache orchestrator
-
| Export | Purpose |
|--------|---------|
| `configureMultiTenancyCache(config)` | Registers the base preset builder. Must be called before handler creation. |
@@ -197,25 +170,6 @@ Important: the current runtime does **not** rely on introspection fingerprints t
| `computeBuildKey(pool, schemas, anonRole, roleName)` | Compute the exact-match handler identity. |
| `getBuildKeyForSvcKey(svcKey)` | Resolve the buildKey currently mapped to a route key. |
-### Introspection cache
-
-| Export | Purpose |
-|--------|---------|
-| `getOrCreateIntrospection(pool, schemas, connectionKey)` | Get or build a cached introspection result. |
-| `invalidateIntrospection(connectionKey, schemas?)` | Invalidate one or all entries for a connection key. |
-| `clearIntrospectionCache()` | Clear the introspection cache completely. |
-| `getIntrospectionCacheStats()` | Return introspection cache stats. |
-| `getConnectionKey(pool)` | Derive the connection cache key for a pool. |
-
-### Lower-level utilities
-
-| Export | Purpose |
-|--------|---------|
-| `getSchemaFingerprint(...)` | Fingerprint helper over parsed introspection data. |
-| `fetchIntrospection(...)` | Fetch raw introspection from PostgreSQL. |
-| `parseIntrospection(...)` | Parse introspection payload into the normalized structure used by this package. |
-| `fetchAndParseIntrospection(...)` | Convenience helper for fetch + parse. |
-
## License
MIT
diff --git a/graphile/graphile-multi-tenancy-cache/package.json b/graphile/graphile-multi-tenancy-cache/package.json
index 224bdf1175..a0e33b1a73 100644
--- a/graphile/graphile-multi-tenancy-cache/package.json
+++ b/graphile/graphile-multi-tenancy-cache/package.json
@@ -2,7 +2,7 @@
"name": "graphile-multi-tenancy-cache",
"version": "0.1.0",
"author": "Constructive ",
- "description": "Multi-tenancy cache utilities for PostGraphile — exact-match buildKey handler reuse with introspection-cache helpers",
+ "description": "Multi-tenancy cache utilities for PostGraphile — exact-match buildKey handler reuse",
"main": "index.js",
"module": "esm/index.js",
"types": "index.d.ts",
@@ -31,7 +31,6 @@
"multi-tenancy",
"cache",
"buildkey",
- "introspection",
"constructive"
],
"bugs": {
@@ -43,10 +42,6 @@
"grafserv": "1.0.0",
"graphile-config": "1.0.0",
"pg": "^8.11.3",
- "pg-env": "workspace:^",
- "pg-introspection": "1.0.0",
- "pgsql-deparser": "^17.18.2",
- "pgsql-parser": "^17.9.14",
"postgraphile": "5.0.0"
},
"devDependencies": {
diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/fingerprint.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/fingerprint.test.ts
deleted file mode 100644
index b743697d6c..0000000000
--- a/graphile/graphile-multi-tenancy-cache/src/__tests__/fingerprint.test.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import { getSchemaFingerprint, type MinimalIntrospection } from '../utils/fingerprint';
-
-/**
- * Helper: build a minimal introspection fixture for two tenants
- * with identical structure but different schema names.
- */
-function makeIntrospection(schemaName: string, schemaOid: string): MinimalIntrospection {
- return {
- namespaces: [
- { nspname: schemaName, oid: schemaOid },
- { nspname: 'pg_catalog', oid: '11' },
- ],
- classes: [
- { relname: 'users', relnamespace: schemaOid, relkind: 'r' },
- { relname: 'posts', relnamespace: schemaOid, relkind: 'r' },
- ],
- attributes: [
- { attrelid: 'users', attname: 'id', atttypid: '23', attnum: 1, attnotnull: true },
- { attrelid: 'users', attname: 'name', atttypid: '25', attnum: 2, attnotnull: false },
- { attrelid: 'posts', attname: 'id', atttypid: '23', attnum: 1, attnotnull: true },
- { attrelid: 'posts', attname: 'title', atttypid: '25', attnum: 2, attnotnull: false },
- { attrelid: 'posts', attname: 'user_id', atttypid: '23', attnum: 3, attnotnull: true },
- ],
- constraints: [
- { conname: 'users_pkey', conrelid: 'users', contype: 'p', conkey: [1], confrelid: null, confkey: null },
- { conname: 'posts_pkey', conrelid: 'posts', contype: 'p', conkey: [1], confrelid: null, confkey: null },
- { conname: 'posts_user_id_fkey', conrelid: 'posts', contype: 'f', conkey: [3], confrelid: 'users', confkey: [1] },
- ],
- types: [
- { typname: 'int4', typnamespace: schemaOid, typtype: 'b' },
- ],
- procs: [
- { proname: 'get_user', pronamespace: schemaOid, proargtypes: '23', prorettype: '2249', provolatile: 's' },
- ],
- };
-}
-
-describe('getSchemaFingerprint', () => {
- it('produces the same fingerprint for identical structures with different schema names', () => {
- const tenant1 = makeIntrospection('t_1_services_public', '100');
- const tenant2 = makeIntrospection('t_2_services_public', '200');
-
- const fp1 = getSchemaFingerprint(tenant1, ['t_1_services_public']);
- const fp2 = getSchemaFingerprint(tenant2, ['t_2_services_public']);
-
- expect(fp1).toBe(fp2);
- });
-
- it('produces different fingerprints when table structure differs', () => {
- const tenant1 = makeIntrospection('t_1_services_public', '100');
- const tenant2 = makeIntrospection('t_2_services_public', '200');
-
- // Add an extra column to tenant2
- tenant2.attributes.push({
- attrelid: 'posts',
- attname: 'body',
- atttypid: '25',
- attnum: 4,
- attnotnull: false,
- });
-
- const fp1 = getSchemaFingerprint(tenant1, ['t_1_services_public']);
- const fp2 = getSchemaFingerprint(tenant2, ['t_2_services_public']);
-
- expect(fp1).not.toBe(fp2);
- });
-
- it('produces different fingerprints when constraint structure differs', () => {
- const tenant1 = makeIntrospection('t_1_services_public', '100');
- const tenant2 = makeIntrospection('t_2_services_public', '200');
-
- // Add a unique constraint to tenant2
- tenant2.constraints.push({
- conname: 'users_name_unique',
- conrelid: 'users',
- contype: 'u',
- conkey: [2],
- confrelid: null,
- confkey: null,
- });
-
- const fp1 = getSchemaFingerprint(tenant1, ['t_1_services_public']);
- const fp2 = getSchemaFingerprint(tenant2, ['t_2_services_public']);
-
- expect(fp1).not.toBe(fp2);
- });
-
- it('returns a valid SHA-256 hex string', () => {
- const introspection = makeIntrospection('public', '100');
- const fp = getSchemaFingerprint(introspection);
-
- expect(fp).toMatch(/^[a-f0-9]{64}$/);
- });
-
- it('is deterministic for the same input', () => {
- const introspection = makeIntrospection('public', '100');
-
- const fp1 = getSchemaFingerprint(introspection);
- const fp2 = getSchemaFingerprint(introspection);
-
- expect(fp1).toBe(fp2);
- });
-
- it('filters by schemaNames when provided', () => {
- const introspection: MinimalIntrospection = {
- namespaces: [
- { nspname: 'schema_a', oid: '100' },
- { nspname: 'schema_b', oid: '200' },
- ],
- classes: [
- { relname: 'users', relnamespace: '100', relkind: 'r' },
- { relname: 'orders', relnamespace: '200', relkind: 'r' },
- ],
- attributes: [],
- constraints: [],
- types: [],
- procs: [],
- };
-
- const fpA = getSchemaFingerprint(introspection, ['schema_a']);
- const fpBoth = getSchemaFingerprint(introspection, ['schema_a', 'schema_b']);
-
- expect(fpA).not.toBe(fpBoth);
- });
-});
diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts
deleted file mode 100644
index 7cb0e5d33b..0000000000
--- a/graphile/graphile-multi-tenancy-cache/src/__tests__/introspection-cache.test.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import {
- getConnectionKey,
- invalidateIntrospection,
- clearIntrospectionCache,
- getIntrospectionCacheStats,
-} from '../introspection-cache';
-
-afterEach(() => {
- clearIntrospectionCache();
-});
-
-describe('introspection-cache', () => {
- describe('getConnectionKey', () => {
- it('returns a hex string from pool options', () => {
- const mockPool = {
- options: {
- host: 'localhost',
- port: 5432,
- database: 'testdb',
- user: 'testuser',
- ssl: false,
- },
- } as any;
-
- const key = getConnectionKey(mockPool);
- expect(key).toMatch(/^[a-f0-9]{16}$/);
- });
-
- it('returns same key for same pool options', () => {
- const pool1 = { options: { host: 'localhost', port: 5432, database: 'db1', user: 'u', ssl: false } } as any;
- const pool2 = { options: { host: 'localhost', port: 5432, database: 'db1', user: 'u', ssl: false } } as any;
-
- expect(getConnectionKey(pool1)).toBe(getConnectionKey(pool2));
- });
-
- it('returns different keys for different databases', () => {
- const pool1 = { options: { host: 'localhost', port: 5432, database: 'db1', user: 'u', ssl: false } } as any;
- const pool2 = { options: { host: 'localhost', port: 5432, database: 'db2', user: 'u', ssl: false } } as any;
-
- expect(getConnectionKey(pool1)).not.toBe(getConnectionKey(pool2));
- });
-
- it('returns different keys for different hosts', () => {
- const pool1 = { options: { host: 'host1', port: 5432, database: 'db', user: 'u', ssl: false } } as any;
- const pool2 = { options: { host: 'host2', port: 5432, database: 'db', user: 'u', ssl: false } } as any;
-
- expect(getConnectionKey(pool1)).not.toBe(getConnectionKey(pool2));
- });
-
- it('distinguishes ssl vs nossl', () => {
- const pool1 = { options: { host: 'h', port: 5432, database: 'db', user: 'u', ssl: false } } as any;
- const pool2 = { options: { host: 'h', port: 5432, database: 'db', user: 'u', ssl: true } } as any;
-
- expect(getConnectionKey(pool1)).not.toBe(getConnectionKey(pool2));
- });
- });
-
- describe('invalidateIntrospection', () => {
- it('does not throw when invalidating non-existent key', () => {
- expect(() => invalidateIntrospection('nonexistent')).not.toThrow();
- });
-
- it('does not throw when invalidating with schemas', () => {
- expect(() => invalidateIntrospection('nonexistent', ['public'])).not.toThrow();
- });
- });
-
- describe('clearIntrospectionCache', () => {
- it('resets stats to zero', () => {
- clearIntrospectionCache();
- const stats = getIntrospectionCacheStats();
- expect(stats.size).toBe(0);
- expect(stats.inflightCount).toBe(0);
- });
- });
-
- describe('getIntrospectionCacheStats', () => {
- it('returns stats object with expected shape', () => {
- const stats = getIntrospectionCacheStats();
- expect(stats).toHaveProperty('size');
- expect(stats).toHaveProperty('maxSize');
- expect(stats).toHaveProperty('inflightCount');
- expect(typeof stats.size).toBe('number');
- expect(typeof stats.maxSize).toBe('number');
- });
- });
-});
diff --git a/graphile/graphile-multi-tenancy-cache/src/index.ts b/graphile/graphile-multi-tenancy-cache/src/index.ts
index 9788d76c4f..03a60b3626 100644
--- a/graphile/graphile-multi-tenancy-cache/src/index.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/index.ts
@@ -17,22 +17,3 @@ export type {
MultiTenancyCacheStats,
MultiTenancyCacheConfig,
} from './multi-tenancy-cache';
-
-// --- Introspection cache (kept as module/test base — not required for the handler runtime) ---
-export {
- getOrCreateIntrospection,
- invalidateIntrospection,
- clearIntrospectionCache,
- getIntrospectionCacheStats,
- getConnectionKey,
-} from './introspection-cache';
-
-export type {
- CachedIntrospection,
- IntrospectionCacheStats,
-} from './introspection-cache';
-
-// --- Utilities (kept as module base — not required for the handler runtime) ---
-export { getSchemaFingerprint } from './utils/fingerprint';
-export type { MinimalIntrospection } from './utils/fingerprint';
-export { fetchIntrospection, parseIntrospection, fetchAndParseIntrospection } from './utils/introspection-query';
diff --git a/graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts b/graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts
deleted file mode 100644
index 4688beec5c..0000000000
--- a/graphile/graphile-multi-tenancy-cache/src/introspection-cache.ts
+++ /dev/null
@@ -1,232 +0,0 @@
-import crypto from 'node:crypto';
-import { Logger } from '@pgpmjs/logger';
-import type { Pool } from 'pg';
-import { fetchAndParseIntrospection } from './utils/introspection-query';
-import { getSchemaFingerprint, type MinimalIntrospection } from './utils/fingerprint';
-
-const log = new Logger('introspection-cache');
-
-// --- Configuration ---
-const MAX_ENTRIES = 100;
-const TTL_MS = 30 * 60 * 1000; // 30 minutes idle
-const SWEEP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
-
-// Test-only hook
-let maxEntries = MAX_ENTRIES;
-
-// --- Types ---
-
-export interface CachedIntrospection {
- parsed: MinimalIntrospection;
- fingerprint: string;
- raw: string;
- createdAt: number;
- lastUsedAt: number;
-}
-
-export interface IntrospectionCacheStats {
- size: number;
- maxSize: number;
- inflightCount: number;
-}
-
-// --- Internal state ---
-
-const cache = new Map();
-const inflight = new Map>();
-let sweepTimer: ReturnType | null = null;
-
-/**
- * Compute a connection hash from pool options.
- * Canonicalized + hashed to avoid leaking credentials while
- * preventing cross-environment collisions.
- */
-function computeConnectionHash(pool: Pool): string {
- const opts = (pool as any).options || {};
- const parts = [
- opts.host || 'localhost',
- String(opts.port || 5432),
- opts.database || '',
- opts.user || '',
- opts.ssl ? 'ssl' : 'nossl',
- ];
- return crypto.createHash('sha256').update(parts.join(':')).digest('hex').slice(0, 16);
-}
-
-/**
- * Build the cache key: connHash:schema1,schema2 (sorted alphabetically).
- */
-function buildCacheKey(connectionKey: string, schemas: string[]): string {
- const sorted = [...schemas].sort();
- return `${connectionKey}:${sorted.join(',')}`;
-}
-
-/**
- * Derive a connection key from a pool.
- */
-export function getConnectionKey(pool: Pool): string {
- return computeConnectionHash(pool);
-}
-
-function ensureSweepTimer(): void {
- if (sweepTimer) return;
- sweepTimer = setInterval(() => {
- sweepIntrospectionCache();
- }, SWEEP_INTERVAL_MS);
- if (sweepTimer.unref) sweepTimer.unref();
-}
-
-// --- Public API ---
-
-/**
- * Get or create a cached introspection result.
- *
- * Single-flight: concurrent requests for the same key coalesce.
- * Failed entries are NOT cached.
- *
- * @param pool - PostgreSQL connection pool
- * @param schemas - Schema names to introspect
- * @param connectionKey - Pre-computed connection key (use getConnectionKey())
- * @returns Cached introspection with fingerprint
- */
-export async function getOrCreateIntrospection(
- pool: Pool,
- schemas: string[],
- connectionKey: string,
-): Promise {
- const cacheKey = buildCacheKey(connectionKey, schemas);
-
- // Cache hit
- const existing = cache.get(cacheKey);
- if (existing) {
- existing.lastUsedAt = Date.now();
- return existing;
- }
-
- // Single-flight coalesce
- const pending = inflight.get(cacheKey);
- if (pending) {
- return pending;
- }
-
- // Cache miss — create
- const promise = doIntrospect(pool, schemas, cacheKey);
- inflight.set(cacheKey, promise);
-
- try {
- const result = await promise;
- return result;
- } finally {
- inflight.delete(cacheKey);
- }
-}
-
-async function doIntrospect(
- pool: Pool,
- schemas: string[],
- cacheKey: string,
-): Promise {
- const { raw, parsed } = await fetchAndParseIntrospection(pool, schemas);
- const fingerprint = getSchemaFingerprint(parsed, schemas);
-
- const entry: CachedIntrospection = {
- parsed,
- fingerprint,
- raw,
- createdAt: Date.now(),
- lastUsedAt: Date.now(),
- };
-
- cache.set(cacheKey, entry);
- ensureSweepTimer();
-
- log.debug(`Cached introspection key=${cacheKey} fingerprint=${fingerprint.slice(0, 12)}…`);
- return entry;
-}
-
-/**
- * Targeted invalidation by connection key and optional schemas.
- */
-export function invalidateIntrospection(
- connectionKey: string,
- schemas?: string[],
-): void {
- if (schemas) {
- const cacheKey = buildCacheKey(connectionKey, schemas);
- cache.delete(cacheKey);
- log.debug(`Invalidated introspection key=${cacheKey}`);
- } else {
- // Invalidate all entries matching this connection key
- const prefix = `${connectionKey}:`;
- for (const key of cache.keys()) {
- if (key.startsWith(prefix)) {
- cache.delete(key);
- }
- }
- log.debug(`Invalidated all introspection entries for connKey=${connectionKey}`);
- }
-}
-
-/**
- * Full cache clear + stop sweep timer.
- */
-export function clearIntrospectionCache(): void {
- cache.clear();
- inflight.clear();
- if (sweepTimer) {
- clearInterval(sweepTimer);
- sweepTimer = null;
- }
- log.debug('Introspection cache cleared');
-}
-
-/**
- * Evict expired + over-cap entries.
- */
-export function sweepIntrospectionCache(): void {
- const now = Date.now();
- const expired: string[] = [];
-
- for (const [key, entry] of cache) {
- if (now - entry.lastUsedAt > TTL_MS) {
- expired.push(key);
- }
- }
-
- for (const key of expired) {
- cache.delete(key);
- }
-
- // LRU cap
- if (cache.size > maxEntries) {
- const sorted = [...cache.entries()].sort(
- (a, b) => a[1].lastUsedAt - b[1].lastUsedAt,
- );
- const toEvict = sorted.slice(0, cache.size - maxEntries);
- for (const [key] of toEvict) {
- cache.delete(key);
- }
- }
-
- if (expired.length > 0 || cache.size > maxEntries) {
- log.debug(`Introspection cache sweep: evicted=${expired.length} size=${cache.size}`);
- }
-}
-
-/**
- * Get diagnostic stats.
- */
-export function getIntrospectionCacheStats(): IntrospectionCacheStats {
- return {
- size: cache.size,
- maxSize: maxEntries,
- inflightCount: inflight.size,
- };
-}
-
-/**
- * Test-only hook to set max entries.
- */
-export function _testSetMaxEntries(n: number): void {
- maxEntries = n;
-}
diff --git a/graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts b/graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts
deleted file mode 100644
index 8b669096d9..0000000000
--- a/graphile/graphile-multi-tenancy-cache/src/utils/fingerprint.ts
+++ /dev/null
@@ -1,175 +0,0 @@
-import crypto from 'node:crypto';
-
-/**
- * Minimal introspection types — just enough for fingerprinting.
- * These mirror pg-introspection's types but only include
- * the fields needed for structural comparison.
- */
-
-export interface IntrospectionNamespace {
- nspname: string;
- oid: string;
-}
-
-export interface IntrospectionClass {
- relname: string;
- relnamespace: string;
- relkind: string;
-}
-
-export interface IntrospectionAttribute {
- attrelid: string;
- attname: string;
- atttypid: string;
- attnum: number;
- attnotnull: boolean;
-}
-
-export interface IntrospectionConstraint {
- conname: string;
- conrelid: string;
- contype: string;
- conkey: number[] | null;
- confrelid: string | null;
- confkey: number[] | null;
-}
-
-export interface IntrospectionType {
- typname: string;
- typnamespace: string;
- typtype: string;
-}
-
-export interface IntrospectionProc {
- proname: string;
- pronamespace: string;
- proargtypes: string;
- prorettype: string;
- provolatile: string;
-}
-
-export interface MinimalIntrospection {
- namespaces: IntrospectionNamespace[];
- classes: IntrospectionClass[];
- attributes: IntrospectionAttribute[];
- constraints: IntrospectionConstraint[];
- types: IntrospectionType[];
- procs: IntrospectionProc[];
-}
-
-/**
- * Compute a schema-name-agnostic structural fingerprint.
- *
- * Included in fingerprint: table names, column names, data types,
- * constraints, function signatures.
- *
- * Excluded: schema/namespace names, OIDs, instance-specific identifiers.
- * This ensures t_1_services_public.apis and t_2_services_public.apis
- * produce the same fingerprint.
- *
- * @param introspection - Parsed introspection result
- * @param schemaNames - Optional list of schema names to filter by
- * @returns SHA-256 hex string
- */
-export function getSchemaFingerprint(
- introspection: MinimalIntrospection,
- schemaNames?: string[],
-): string {
- const schemaOids = new Set();
-
- if (schemaNames && schemaNames.length > 0) {
- const nameSet = new Set(schemaNames);
- for (const ns of introspection.namespaces) {
- if (nameSet.has(ns.nspname)) {
- schemaOids.add(ns.oid);
- }
- }
- } else {
- for (const ns of introspection.namespaces) {
- schemaOids.add(ns.oid);
- }
- }
-
- // Filter classes to target schemas
- const classes = introspection.classes
- .filter((c) => schemaOids.has(c.relnamespace))
- .sort((a, b) => a.relname.localeCompare(b.relname));
-
- const classOids = new Set(classes.map((c) => (c as any).oid || c.relname));
-
- // Normalize tables: name + kind (strip schema)
- const tables = classes.map((c) => `${c.relname}:${c.relkind}`);
-
- // Normalize columns: tableName.colName:typeOid:notNull:attNum
- const columns = introspection.attributes
- .filter((a) => {
- // Find the class this attribute belongs to
- const cls = introspection.classes.find(
- (c) => ((c as any).oid || c.relname) === a.attrelid,
- );
- return cls && schemaOids.has(cls.relnamespace);
- })
- .sort((a, b) => {
- if (a.attrelid !== b.attrelid) return a.attrelid.localeCompare(b.attrelid);
- return a.attnum - b.attnum;
- })
- .map((a) => {
- const cls = introspection.classes.find(
- (c) => ((c as any).oid || c.relname) === a.attrelid,
- );
- const tableName = cls?.relname || a.attrelid;
- return `${tableName}.${a.attname}:${a.atttypid}:${a.attnotnull}:${a.attnum}`;
- });
-
- // Normalize constraints: sorted by name, with type and key columns
- const constraints = introspection.constraints
- .filter((c) => {
- const cls = introspection.classes.find(
- (cl) => ((cl as any).oid || cl.relname) === c.conrelid,
- );
- return cls && schemaOids.has(cls.relnamespace);
- })
- .sort((a, b) => a.conname.localeCompare(b.conname))
- .map((c) => {
- const cls = introspection.classes.find(
- (cl) => ((cl as any).oid || cl.relname) === c.conrelid,
- );
- const tableName = cls?.relname || c.conrelid;
- const keys = c.conkey ? c.conkey.sort().join(',') : '';
- const fkeys = c.confkey ? c.confkey.sort().join(',') : '';
- return `${tableName}.${c.conname}:${c.contype}:${keys}:${fkeys}`;
- });
-
- // Normalize types: name + kind (strip namespace)
- const types = introspection.types
- .filter((t) => schemaOids.has(t.typnamespace))
- .sort((a, b) => a.typname.localeCompare(b.typname))
- .map((t) => `${t.typname}:${t.typtype}`);
-
- // Normalize procs: name + arg types + return type + volatility (strip namespace)
- const procs = introspection.procs
- .filter((p) => schemaOids.has(p.pronamespace))
- .sort((a, b) => {
- if (a.proname !== b.proname) return a.proname.localeCompare(b.proname);
- return a.proargtypes.localeCompare(b.proargtypes);
- })
- .map((p) => `${p.proname}:${p.proargtypes}:${p.prorettype}:${p.provolatile}`);
-
- // Build canonical string and hash
- const canonical = [
- `tables:${tables.join('|')}`,
- `columns:${columns.join('|')}`,
- `constraints:${constraints.join('|')}`,
- `types:${types.join('|')}`,
- `procs:${procs.join('|')}`,
- ].join('\n');
-
- return crypto.createHash('sha256').update(canonical).digest('hex');
-}
-
-/**
- * Compare two fingerprints for equality.
- */
-export function fingerprintsMatch(a: string, b: string): boolean {
- return a === b;
-}
diff --git a/graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts b/graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts
deleted file mode 100644
index 32cf2ef533..0000000000
--- a/graphile/graphile-multi-tenancy-cache/src/utils/introspection-query.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import { Logger } from '@pgpmjs/logger';
-import type { Pool } from 'pg';
-
-const log = new Logger('introspection-query');
-
-/**
- * Low-level introspection fetch + parse.
- *
- * Queries pg_catalog tables directly to get schema structure.
- * Uses BEGIN + SET LOCAL search_path + COMMIT so the search_path
- * never leaks to pooled connections.
- */
-
-/**
- * Fetch raw introspection JSON from the database.
- *
- * @param pool - PostgreSQL connection pool
- * @param schemas - Schema names to introspect
- * @returns Raw JSON string containing introspection data
- */
-export async function fetchIntrospection(
- pool: Pool,
- schemas: string[],
-): Promise {
- const client = await pool.connect();
- try {
- await client.query('BEGIN');
- await client.query(`SET LOCAL search_path TO ${schemas.map((s) => `"${s}"`).join(', ')}`);
-
- const result = await client.query(`
- SELECT json_build_object(
- 'namespaces', (
- SELECT coalesce(json_agg(row_to_json(n)), '[]'::json)
- FROM pg_catalog.pg_namespace n
- WHERE n.nspname = ANY($1)
- ),
- 'classes', (
- SELECT coalesce(json_agg(row_to_json(c)), '[]'::json)
- FROM pg_catalog.pg_class c
- JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
- WHERE n.nspname = ANY($1)
- AND c.relkind IN ('r', 'v', 'm', 'f', 'p')
- ),
- 'attributes', (
- SELECT coalesce(json_agg(row_to_json(a)), '[]'::json)
- FROM pg_catalog.pg_attribute a
- JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
- JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
- WHERE n.nspname = ANY($1)
- AND a.attnum > 0
- AND NOT a.attisdropped
- ),
- 'constraints', (
- SELECT coalesce(json_agg(row_to_json(co)), '[]'::json)
- FROM pg_catalog.pg_constraint co
- JOIN pg_catalog.pg_class c ON co.conrelid = c.oid
- JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
- WHERE n.nspname = ANY($1)
- ),
- 'types', (
- SELECT coalesce(json_agg(row_to_json(t)), '[]'::json)
- FROM pg_catalog.pg_type t
- JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
- WHERE n.nspname = ANY($1)
- ),
- 'procs', (
- SELECT coalesce(json_agg(row_to_json(p)), '[]'::json)
- FROM pg_catalog.pg_proc p
- JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
- WHERE n.nspname = ANY($1)
- )
- ) AS introspection
- `, [schemas]);
-
- await client.query('COMMIT');
-
- return JSON.stringify(result.rows[0].introspection);
- } catch (err) {
- await client.query('ROLLBACK').catch(() => {});
- log.error('Introspection query failed', err);
- throw err;
- } finally {
- client.release();
- }
-}
-
-/**
- * Parse a raw introspection JSON string into structured data.
- *
- * @param text - Raw JSON string from fetchIntrospection
- * @returns Parsed introspection result
- */
-export function parseIntrospection(text: string): import('./fingerprint').MinimalIntrospection {
- try {
- const data = JSON.parse(text);
- return {
- namespaces: data.namespaces || [],
- classes: data.classes || [],
- attributes: data.attributes || [],
- constraints: data.constraints || [],
- types: data.types || [],
- procs: data.procs || [],
- };
- } catch (err) {
- log.error('Failed to parse introspection JSON', err);
- throw err;
- }
-}
-
-/**
- * Fetch and parse introspection data in one call.
- *
- * @param pool - PostgreSQL connection pool
- * @param schemas - Schema names to introspect
- * @returns Object with raw JSON string and parsed data
- */
-export async function fetchAndParseIntrospection(
- pool: Pool,
- schemas: string[],
-): Promise<{ raw: string; parsed: import('./fingerprint').MinimalIntrospection }> {
- const raw = await fetchIntrospection(pool, schemas);
- const parsed = parseIntrospection(raw);
- return { raw, parsed };
-}
diff --git a/graphql/server/perf/E2E_BENCHMARK_REPORT.md b/graphql/server/perf/E2E_BENCHMARK_REPORT.md
index bfb945f530..bfac913a56 100644
--- a/graphql/server/perf/E2E_BENCHMARK_REPORT.md
+++ b/graphql/server/perf/E2E_BENCHMARK_REPORT.md
@@ -4,7 +4,6 @@ This document captures the stress-test results for the current multi-tenancy cac
The optimization logic evaluated here is:
-- introspection caching
- exact-match buildKey handler reuse
- perf stress testing of the old vs new request path
@@ -71,7 +70,7 @@ Each test was run in OLD mode and NEW mode.
### Why the heap savings are so large
-The dominant effect is exact-match buildKey deduplication combined with cached introspection state.
+The dominant effect is exact-match buildKey deduplication.
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 single handler.
@@ -97,7 +96,6 @@ The benchmark exercises full HTTP traffic through Express, PostGraphile, Grafast
The new path still wins because:
- fewer handlers means less GC pressure
-- repeated introspection/bootstrap work is reduced
- working-set size is smaller under load
- hot handlers stay reused across many route keys
@@ -128,7 +126,6 @@ The 2-hour soak is the strongest signal here: millions of queries, repeated flus
| Factor | Heap Impact | QPS Impact | Stability Impact |
|--------|-------------|------------|------------------|
| buildKey deduplication | dominant | moderate | — |
-| introspection caching | secondary | secondary | — |
| reduced GC pressure | secondary | primary at higher fanout | — |
| deferred registration | leak prevention | — | critical |
| rebinding cleanup | leak prevention | — | critical |
@@ -136,7 +133,7 @@ The 2-hour soak is the strongest signal here: millions of queries, repeated flus
## Conclusion
-The current strategy of introspection caching plus exact-match buildKey handler reuse delivers substantial memory savings while preserving stable throughput and correctness under load.
+The current strategy of exact-match buildKey handler reuse delivers substantial memory savings while preserving stable throughput and correctness under load.
For this workload pattern, the results show:
diff --git a/graphql/server/perf/README.md b/graphql/server/perf/README.md
index 0d90bd34da..c467a520fe 100644
--- a/graphql/server/perf/README.md
+++ b/graphql/server/perf/README.md
@@ -4,7 +4,6 @@ This directory contains the performance tooling for the GraphQL server.
The current optimization model being exercised is:
-- introspection caching
- exact-match buildKey handler reuse
- no template sharing
- no SQL rewrite
@@ -74,6 +73,8 @@ The intended usage is:
3. Keep the main CRUD workload pointed only at the original business table.
4. Use `summarize-shapes.mjs` to inspect structural divergence without relying on raw GraphQL introspection names.
+`summarize-shapes.mjs` is a perf-local diagnostic. It is not part of the runtime handler reuse mechanism.
+
## Quick Start
### Old vs New comparison
diff --git a/graphql/server/perf/phase2-load.mjs b/graphql/server/perf/phase2-load.mjs
index 60d5712c26..645b2ac30b 100644
--- a/graphql/server/perf/phase2-load.mjs
+++ b/graphql/server/perf/phase2-load.mjs
@@ -201,6 +201,8 @@ const summarizeCacheRedundancy = (
{ 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();
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 646b1bc062..496e7eb4d5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -355,18 +355,6 @@ importers:
pg:
specifier: ^8.11.3
version: 8.20.0
- pg-env:
- specifier: workspace:^
- version: link:../../postgres/pg-env/dist
- pg-introspection:
- specifier: 1.0.0
- version: 1.0.0
- pgsql-deparser:
- specifier: ^17.18.2
- version: 17.18.2
- pgsql-parser:
- specifier: ^17.9.14
- version: 17.9.14
postgraphile:
specifier: 5.0.0
version: 5.0.0(f35d86129e8192df0ebe9574df8f7655)
From abb1a036f2402e75b3be2710a624eafda426e180 Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 21 Apr 2026 08:53:05 +0800
Subject: [PATCH 05/17] update comment
---
graphql/types/src/graphile.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/graphql/types/src/graphile.ts b/graphql/types/src/graphile.ts
index 4cf4548edc..fa6b542a03 100644
--- a/graphql/types/src/graphile.ts
+++ b/graphql/types/src/graphile.ts
@@ -42,7 +42,7 @@ export interface ApiOptions {
isPublic?: boolean;
/** Schemas containing metadata tables */
metaSchemas?: string[];
- /** Enable multi-tenancy cache (template-based instance sharing across tenants) */
+ /** Enable multi-tenancy cache (buildKey-based handler reuse across matching inputs) */
useMultiTenancyCache?: boolean;
}
From ccc32a14d27f4385e5fefa727356160d6c9548b2 Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 21 Apr 2026 10:35:32 +0800
Subject: [PATCH 06/17] fixed the buildkey collision minor possibility
---
.../graphile-multi-tenancy-cache/README.md | 4 +++-
.../src/__tests__/buildkey.test.ts | 22 ++++++++++++++----
.../src/multi-tenancy-cache.ts | 23 ++++++++++++++-----
3 files changed, 37 insertions(+), 12 deletions(-)
diff --git a/graphile/graphile-multi-tenancy-cache/README.md b/graphile/graphile-multi-tenancy-cache/README.md
index c771c23006..2bfa53034e 100644
--- a/graphile/graphile-multi-tenancy-cache/README.md
+++ b/graphile/graphile-multi-tenancy-cache/README.md
@@ -100,7 +100,7 @@ process.on('SIGTERM', async () => {
| Concept | Meaning |
|--------|---------|
| `svc_key` | Request routing key. Used to look up which cached handler the current request should hit. |
-| `buildKey` | Handler identity. Computed from the inputs that materially affect Graphile instance construction. |
+| `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
@@ -119,6 +119,8 @@ It does **not** include:
- 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.
## How the handler cache works
diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
index 7e9977ab6f..fc9c1856e1 100644
--- a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
@@ -10,8 +10,6 @@
* - shutdown clears all state
*/
-import { createHash } from 'node:crypto';
-
// We test computeBuildKey directly and use mocks for the orchestrator functions
// that depend on PostGraphile.
@@ -51,10 +49,17 @@ describe('computeBuildKey', () => {
expect(k1).toBe(k2);
});
- it('should produce a 16-char hex string', () => {
+ it('should produce a canonical JSON string', () => {
const pool = makeMockPool();
const key = computeBuildKey(pool, ['public'], 'anon', 'authenticated');
- expect(key).toMatch(/^[0-9a-f]{16}$/);
+ expect(key).toBe(
+ JSON.stringify({
+ conn: 'localhost:5432/testdb@postgres',
+ schemas: ['public'],
+ anonRole: 'anon',
+ roleName: 'authenticated',
+ }),
+ );
});
it('should differ when schemas differ', () => {
@@ -651,7 +656,14 @@ describe('connectionString-based pool identity', () => {
// 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).toMatch(/^[0-9a-f]{16}$/);
+ expect(key).toBe(
+ JSON.stringify({
+ conn: 'myhost:5432/mydb@myuser',
+ schemas: ['public'],
+ anonRole: 'anon',
+ roleName: 'auth',
+ }),
+ );
});
});
diff --git a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
index 944cb38324..f117831755 100644
--- a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
@@ -1,8 +1,9 @@
/**
* Multi-tenancy cache orchestrator.
*
- * Caches one independent PostGraphile handler per **buildKey** (derived from
- * the inputs that materially affect Graphile handler construction).
+ * 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.
@@ -15,7 +16,6 @@
* databaseIdToBuildKeys: databaseId → Set
*/
-import { createHash } from 'node:crypto';
import { createServer } from 'node:http';
import { Logger } from '@pgpmjs/logger';
import express from 'express';
@@ -54,6 +54,13 @@ export interface MultiTenancyCacheStats {
inflightCreations: number;
}
+interface BuildKeyParts {
+ conn: string;
+ schemas: string[];
+ anonRole: string;
+ roleName: string;
+}
+
// --- Internal state ---
/** buildKey → TenantInstance (the real handler cache) */
@@ -163,6 +170,10 @@ function getPoolIdentity(pool: Pool): string {
* - svc_key (routing-only)
* - databaseId (metadata-only)
* - token data, host/domain, transient headers
+ *
+ * The buildKey is intentionally stored as a canonical plain-text string
+ * rather than a truncated hash so there is no collision risk between
+ * different tenant build inputs.
*/
export function computeBuildKey(
pool: Pool,
@@ -170,13 +181,13 @@ export function computeBuildKey(
anonRole: string,
roleName: string,
): string {
- const input = JSON.stringify({
+ const input: BuildKeyParts = {
conn: getPoolIdentity(pool),
schemas,
anonRole,
roleName,
- });
- return createHash('sha256').update(input).digest('hex').slice(0, 16);
+ };
+ return JSON.stringify(input);
}
// --- Index management ---
From 7ea068f930c2c3065daa8dfd754a358b369699bf Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 21 Apr 2026 11:38:43 +0800
Subject: [PATCH 07/17] add examples of buildkey to the README
---
.../graphile-multi-tenancy-cache/README.md | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/graphile/graphile-multi-tenancy-cache/README.md b/graphile/graphile-multi-tenancy-cache/README.md
index 2bfa53034e..caafb36a39 100644
--- a/graphile/graphile-multi-tenancy-cache/README.md
+++ b/graphile/graphile-multi-tenancy-cache/README.md
@@ -123,6 +123,61 @@ The value is stored as a canonical plain-text key rather than a truncated hash,
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, and role inputs:
+
+```json
+{"conn":"127.0.0.1:5432/mydb@postgres","schemas":["services_public"],"anonRole":"administrator","roleName":"administrator"}
+```
+
+- 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`
+
+ These then feed into the buildKey:
+
+```json
+{"conn":":/@","schemas":[...],"anonRole":"...","roleName":"..."}
+```
+
+ Different route keys only share a `buildKey` if they ultimately resolve to the same:
+
+ - `conn`
+ - `schemas`
+ - `anonRole`
+ - `roleName`
+
+ 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"}
+{"conn":"127.0.0.1:5432/mydb@postgres","schemas":["metaschema_public","services_public"],"anonRole":"administrator","roleName":"administrator"}
+```
+
+- 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:
From 192fd1de6e640ac75faa30e59530abe310d16727 Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Tue, 16 Jun 2026 17:42:48 +0800
Subject: [PATCH 08/17] merge with main
---
graphql/server/src/server.ts | 103 +-
pnpm-lock.yaml | 3123 +++++++---------------------------
2 files changed, 729 insertions(+), 2497 deletions(-)
diff --git a/graphql/server/src/server.ts b/graphql/server/src/server.ts
index 92322b1dd2..985f4eb93a 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';
@@ -28,7 +34,12 @@ import { cors } from './middleware/cors';
import { errorHandler, notFoundHandler } from './middleware/error-handler';
import { favicon } from './middleware/favicon';
import { flush, createFlushMiddleware, flushService } from './middleware/flush';
-import { graphile, multiTenancyHandler, isMultiTenancyCacheEnabled, shutdownMultiTenancy } from './middleware/graphile';
+import {
+ graphile,
+ multiTenancyHandler,
+ isMultiTenancyCacheEnabled,
+ shutdownMultiTenancy,
+} from './middleware/graphile';
import { multipartBridge } from './middleware/multipart-bridge';
import { createDebugDatabaseMiddleware } from './middleware/observability/debug-db';
import { debugMemory } from './middleware/observability/debug-memory';
@@ -37,9 +48,15 @@ import { createRequestLogger } from './middleware/observability/request-logger';
// Auth cookie handling is done via AuthCookiePlugin in grafserv
import { createCaptchaMiddleware } from './middleware/captcha';
import { parseCookieValue, SESSION_COOKIE_NAME } from './middleware/cookie';
-import { createUploadAuthenticateMiddleware, uploadRoute } from './middleware/upload';
+import {
+ createUploadAuthenticateMiddleware,
+ uploadRoute,
+} from './middleware/upload';
import { createLlmApiRouter } from './middleware/llm-api';
-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');
@@ -65,7 +82,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();
@@ -86,12 +105,15 @@ 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);
const authenticate = createAuthenticateMiddleware(effectiveOpts);
- const uploadAuthenticate = createUploadAuthenticateMiddleware(effectiveOpts);
+ const uploadAuthenticate =
+ createUploadAuthenticateMiddleware(effectiveOpts);
const requestLogger = createRequestLogger({ observabilityEnabled });
// Log startup configuration (non-sensitive values only)
@@ -124,14 +146,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');
@@ -144,20 +170,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);
@@ -170,12 +201,6 @@ class Server {
app.use(authenticate);
app.use(createContextMiddleware({ pg: effectiveOpts.pg }));
app.use(createCaptchaMiddleware());
- // Select handler based on multi-tenancy cache mode
- if (isMultiTenancyCacheEnabled(effectiveOpts)) {
- log.info('[server] Multi-tenancy cache ENABLED');
- app.use(multiTenancyHandler(effectiveOpts));
- app.use(createFlushMiddleware(effectiveOpts));
- } else {
// CSRF protection for cookie-authenticated requests
// Skip CSRF for Bearer token auth (not vulnerable to CSRF) and anonymous requests
@@ -186,7 +211,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 ')) {
@@ -200,7 +229,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
@@ -210,7 +243,13 @@ class Server {
// routes are handled without going through PostGraphile
app.use(createLlmApiRouter());
- app.use(graphile(effectiveOpts));
+ // Select handler based on multi-tenancy cache mode
+ if (isMultiTenancyCacheEnabled(effectiveOpts)) {
+ log.info('[server] Multi-tenancy cache ENABLED');
+ app.use(multiTenancyHandler(effectiveOpts));
+ app.use(createFlushMiddleware(effectiveOpts));
+ } else {
+ app.use(graphile(effectiveOpts));
app.use(flush);
}
@@ -219,13 +258,15 @@ class Server {
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) => {
@@ -255,7 +296,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) {
@@ -332,7 +377,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) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bc3807e1c9..8b4a1296c8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -568,46 +568,7 @@ importers:
version: link:../../postgres/pgsql-test/dist
publishDirectory: dist
-<<<<<<< HEAD
- graphile/graphile-multi-tenancy-cache:
- dependencies:
- '@pgpmjs/logger':
- specifier: workspace:^
- version: link:../../pgpm/logger/dist
- express:
- specifier: ^5.2.1
- version: 5.2.1
- grafserv:
- specifier: 1.0.0
- version: 1.0.0(@types/node@25.5.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(grafast@1.0.0(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))(ws@8.19.0)
- graphile-config:
- specifier: 1.0.0
- version: 1.0.0
- pg:
- specifier: ^8.11.3
- version: 8.20.0
- postgraphile:
- specifier: 5.0.0
- version: 5.0.0(f35d86129e8192df0ebe9574df8f7655)
- devDependencies:
- '@types/express':
- specifier: ^5.0.6
- version: 5.0.6
- '@types/pg':
- specifier: ^8.10.9
- version: 8.18.0
- makage:
- specifier: ^0.3.0
- version: 0.3.0
- ts-node:
- specifier: ^10.9.2
- version: 10.9.2(@types/node@25.5.0)(typescript@5.9.3)
- publishDirectory: dist
-
- graphile/graphile-postgis:
-=======
graphile/graphile-ltree:
->>>>>>> main
dependencies:
'@dataplan/pg':
specifier: 1.0.3
@@ -651,6 +612,41 @@ 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
+ grafserv:
+ specifier: 1.0.0
+ version: 1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1)
+ graphile-config:
+ specifier: 1.0.0
+ version: 1.0.0
+ pg:
+ specifier: ^8.11.3
+ version: 8.21.0
+ postgraphile:
+ specifier: 5.0.0
+ version: 5.0.0(f3ea149703b7f495547261dad1e3b475)
+ 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':
@@ -1756,19 +1752,14 @@ importers:
specifier: workspace:^
version: link:../../graphile/graphile-cache/dist
graphile-config:
-<<<<<<< HEAD
- specifier: 1.0.0
- version: 1.0.0
- graphile-multi-tenancy-cache:
- specifier: workspace:^
- version: link:../../graphile/graphile-multi-tenancy-cache/dist
-=======
specifier: 1.0.1
version: 1.0.1
graphile-llm:
specifier: workspace:^
version: link:../../graphile/graphile-llm/dist
->>>>>>> main
+ graphile-multi-tenancy-cache:
+ specifier: workspace:^
+ version: link:../../graphile/graphile-multi-tenancy-cache/dist
graphile-settings:
specifier: workspace:^
version: link:../../graphile/graphile-settings/dist
@@ -3627,16 +3618,8 @@ packages:
graphql:
optional: true
-<<<<<<< HEAD
- '@agentic-kit/ollama@1.0.3':
- resolution: {integrity: sha512-bvDMjofjgSTI8oLvFI/8ORQvO+wrwmdYETYCB1yAOSy622zB6DWYqk+ldK7mKjZ22iWrjkoWFEdSY7T9rYE1FA==}
-=======
'@asamuzakjp/css-color@3.2.0':
- resolution:
- {
- integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
'@aws-crypto/crc32@5.2.0':
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
@@ -3661,334 +3644,112 @@ packages:
'@aws-crypto/util@5.2.0':
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
-<<<<<<< HEAD
- '@aws-sdk/client-s3@3.1009.0':
- resolution: {integrity: sha512-luy8CxallkoiGWTqU86ca/BbvkWJjs0oala7uIIRN1JtQxMb5i4Yl/PBZVcQFhbK9kQi0PK0GfD8gIpLkI91fw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/core@3.973.24':
- resolution: {integrity: sha512-vvf82RYQu2GidWAuQq+uIzaPz9V0gSCXVqdVzRosgl5rXcspXOpSD3wFreGGW6AYymPr97Z69kjVnLePBxloDw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/crc64-nvme@3.972.5':
- resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-env@3.972.18':
- resolution: {integrity: sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-http@3.972.20':
- resolution: {integrity: sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-ini@3.972.20':
- resolution: {integrity: sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-login@3.972.20':
- resolution: {integrity: sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-node@3.972.21':
- resolution: {integrity: sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-process@3.972.18':
- resolution: {integrity: sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-sso@3.972.20':
- resolution: {integrity: sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/credential-provider-web-identity@3.972.20':
- resolution: {integrity: sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/lib-storage@3.1009.0':
- resolution: {integrity: sha512-gHQh1sNeTuxZxPSMSQWOq/Xli8I5499uWyRKMakMSv8N7IYfoyDdyT52Ul6697qcqVaoPHixmYTllfEWMo1AKg==}
- engines: {node: '>=20.0.0'}
-=======
'@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' }
->>>>>>> main
+ resolution: {integrity: sha512-7agYCtfeOm3ylg95ysiXnmeGKo1rZY5EzDsD9t02hUL8+oo1wO3DuwF2c3IHdez/hB+QSOSKfmk/ZC5/3ybNHQ==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
'@aws-sdk/client-s3': ^3.1052.0
-<<<<<<< HEAD
- '@aws-sdk/middleware-bucket-endpoint@3.972.8':
- resolution: {integrity: sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-expect-continue@3.972.8':
- resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-flexible-checksums@3.973.6':
- resolution: {integrity: sha512-0nYEgkJH7Yt9k+nZJyllTghnkKaz17TWFcr5Mi0XMVMzYlF4ytDZADQpF2/iJo36cKL5AYSzRsvlykE4M/ErTA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-host-header@3.972.8':
- resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-location-constraint@3.972.8':
- resolution: {integrity: sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-logger@3.972.8':
- resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-recursion-detection@3.972.8':
- resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-sdk-s3@3.972.25':
- resolution: {integrity: sha512-4xJL7O+XkhbSkT4yAYshkAww+mxJvtGQneNHH0MOpe+w8Vo2z87M9z06UO3G6zPM2c3Ef2yKczvZpTgdArMHfg==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-ssec@3.972.8':
- resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/middleware-user-agent@3.972.21':
- resolution: {integrity: sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/nested-clients@3.996.10':
- resolution: {integrity: sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/region-config-resolver@3.972.8':
- resolution: {integrity: sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/s3-request-presigner@3.1017.0':
- resolution: {integrity: sha512-PSR8VJEkCy53uhAeuvht6ub3kzfdqoTAmLliQJ63MkC/1FuMmrmqWRGoZuzZvAbpzTcZtuibSGvawDa47gsckA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/signature-v4-multi-region@3.996.13':
- resolution: {integrity: sha512-7j8rOFHHq4e9McCSuWBmBSADriW5CjPUem4inckRh/cyQGaijBwDbkNbVTgDVDWqFo29SoVVUfI6HCOnck6HZw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/token-providers@3.1009.0':
- resolution: {integrity: sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/types@3.973.6':
- resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/util-arn-parser@3.972.3':
- resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/util-endpoints@3.996.5':
- resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==}
- engines: {node: '>=20.0.0'}
-
- '@aws-sdk/util-format-url@3.972.8':
- resolution: {integrity: sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A==}
- engines: {node: '>=20.0.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' }
->>>>>>> main
+ 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'}
-<<<<<<< HEAD
- '@aws-sdk/util-user-agent-browser@3.972.8':
- resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==}
-
- '@aws-sdk/util-user-agent-node@3.973.7':
- resolution: {integrity: sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==}
- engines: {node: '>=20.0.0'}
- peerDependencies:
- aws-crt: '>=1.0.0'
- peerDependenciesMeta:
- aws-crt:
- optional: true
-
- '@aws-sdk/xml-builder@3.972.15':
- resolution: {integrity: sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA==}
- engines: {node: '>=20.0.0'}
-=======
'@aws-sdk/xml-builder@3.972.25':
- resolution:
- {
- integrity: sha512-GH+Kjz4nPKWKHnsiQpnhP1MJdTGIcK4rAka6tzakgjjUkVgNsmPeEbbRAf09SzS1hjGu6duGHCBsxYke0BhHjQ==,
- }
- engines: { node: '>=20.0.0' }
->>>>>>> main
+ 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'}
-
'@babel/code-frame@7.28.6':
resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==}
engines: {node: '>=6.9.0'}
@@ -4025,13 +3786,6 @@ packages:
resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
engines: {node: '>=6.9.0'}
-<<<<<<< HEAD
- '@babel/helper-module-imports@7.27.1':
- resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
- engines: {node: '>=6.9.0'}
-
-=======
->>>>>>> main
'@babel/helper-module-imports@7.28.6':
resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
engines: {node: '>=6.9.0'}
@@ -4042,13 +3796,6 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0
-<<<<<<< HEAD
- '@babel/helper-plugin-utils@7.27.1':
- resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
- engines: {node: '>=6.9.0'}
-
-=======
->>>>>>> main
'@babel/helper-plugin-utils@7.28.6':
resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
engines: {node: '>=6.9.0'}
@@ -4069,19 +3816,9 @@ packages:
resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==}
engines: {node: '>=6.9.0'}
-<<<<<<< HEAD
- '@babel/parser@7.28.6':
- resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==}
- engines: {node: '>=6.0.0'}
- hasBin: true
-=======
'@babel/helpers@7.29.2':
- resolution:
- {
- integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==,
- }
- engines: { node: '>=6.9.0' }
->>>>>>> main
+ resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==}
+ engines: {node: '>=6.9.0'}
'@babel/parser@7.29.0':
resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
@@ -4089,11 +3826,8 @@ packages:
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':
@@ -4133,15 +3867,6 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
-<<<<<<< HEAD
- '@babel/plugin-syntax-jsx@7.27.1':
- resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
-=======
->>>>>>> main
'@babel/plugin-syntax-jsx@7.28.6':
resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==}
engines: {node: '>=6.9.0'}
@@ -4244,63 +3969,42 @@ packages:
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'}
'@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==}
@@ -4308,18 +4012,9 @@ packages:
peerDependencies:
grafast: ^1.0.0-rc.8
-<<<<<<< HEAD
- '@dataplan/pg@1.0.0':
- resolution: {integrity: sha512-Nl4cdQWgdl86u78K1FjQtvH+AyH5ToDb9hYxN99Hu8T+ip6a6B3i3Ho0nRlBccUWYHx+p92Kh70sDXCJ3Fpmnw==}
- engines: {node: '>=22'}
-=======
'@dataplan/pg@1.0.3':
- resolution:
- {
- integrity: sha512-DdgPF+Mg8KntTAC5lW/4w34s74NLCgBgpbw3+PtBhi2QC66acD7noVWZAgzUx/ATouLE9gUiKqHnva89vcNEjA==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ resolution: {integrity: sha512-DdgPF+Mg8KntTAC5lW/4w34s74NLCgBgpbw3+PtBhi2QC66acD7noVWZAgzUx/ATouLE9gUiKqHnva89vcNEjA==}
+ engines: {node: '>=22'}
peerDependencies:
'@dataplan/json': 1.0.0
grafast: ^1.0.2
@@ -4332,47 +4027,22 @@ 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==}
-<<<<<<< HEAD
- '@emnapi/core@1.9.0':
- resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==}
-=======
'@emnapi/runtime@1.10.0':
- resolution:
- {
- integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
'@emnapi/runtime@1.7.1':
resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==}
-<<<<<<< HEAD
- '@emnapi/runtime@1.9.0':
- resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==}
-
-=======
->>>>>>> main
'@emnapi/wasi-threads@1.1.0':
resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
-<<<<<<< HEAD
- '@emnapi/wasi-threads@1.2.0':
- resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==}
-=======
'@emnapi/wasi-threads@1.2.1':
- resolution:
- {
- integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
'@emotion/is-prop-valid@1.4.0':
resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==}
@@ -4771,12 +4441,6 @@ packages:
resolution: {integrity: sha512-NeRBDdUd/l4H284HrYL2/wNHv/FmW5stAMPFAiBZanLHwq9J3suZTtyN5CwTxUFA/vgqzu0B1/9XtIEaJYEKig==}
engines: {node: '>=22'}
-<<<<<<< HEAD
- '@graphile/simplify-inflection@8.0.0':
- resolution: {integrity: sha512-fzC4zz456Km8oZQMiBU03kgSRQxT+aS3sEdEQIcvkfdUhJKlIk81ll8UaxLLkl/WRb7TByNM1VfrY9mEa4w9kw==}
-
-=======
->>>>>>> main
'@graphiql/plugin-doc-explorer@0.4.1':
resolution: {integrity: sha512-+ram1dDDGMqJn/f9n5I8E6grTvxcM9JZYt/HhtYLuCvkN8kERI6/E3zBHBshhIUnQZoXioZ03fAzXg7JOn0Kyg==}
peerDependencies:
@@ -4852,32 +4516,16 @@ packages:
'@types/node':
optional: true
-<<<<<<< HEAD
- '@inquirerer/test@1.3.5':
- resolution: {integrity: sha512-oA8rqZTDAqgf3GJ48KFX6/cLrnm9D8qzqseU1mJuFibDheb2TpGzR7cshCwQtnc4zuOEAl5QyLrg8BHhX5DR4Q==}
-=======
'@inquirerer/test@1.4.1':
- resolution:
- {
- integrity: sha512-fDHBK1+0Hfz/Ht6Zhs4Bt89HrBX7KtIayurukbUHIPFZaN4BuWdBxvaBwmrLK1Mq88KIgHA2ZxZ4zdc4AmeEvg==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-fDHBK1+0Hfz/Ht6Zhs4Bt89HrBX7KtIayurukbUHIPFZaN4BuWdBxvaBwmrLK1Mq88KIgHA2ZxZ4zdc4AmeEvg==}
peerDependencies:
jest: '>=29.0.0'
peerDependenciesMeta:
jest:
optional: true
-<<<<<<< HEAD
- '@inquirerer/utils@3.3.5':
- resolution: {integrity: sha512-ENzkQImZ59Y2wegY4ng9MsCLe2CZjWGO6WXFpoppGQmZqgIOAzsmGHpdQxORRS+I4RnzA/7Tr+ZUJ6bSpC7JWg==}
-=======
'@inquirerer/utils@3.3.7':
- resolution:
- {
- integrity: sha512-L1M/fwb9VqbYQLorOWw2hgZk2LW4dZ4YsvIj3SJQWNPMmdcDDHDE2wkFK+KT/kh23aGUS7ISM/b0TIiyKeeQQA==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-L1M/fwb9VqbYQLorOWw2hgZk2LW4dZ4YsvIj3SJQWNPMmdcDDHDE2wkFK+KT/kh23aGUS7ISM/b0TIiyKeeQQA==}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
@@ -4894,40 +4542,17 @@ packages:
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
engines: {node: '>=8'}
-<<<<<<< HEAD
- '@istanbuljs/schema@0.1.3':
- resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
- engines: {node: '>=8'}
-
- '@jest/console@30.3.0':
- resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- '@jest/core@30.3.0':
- resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-=======
'@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 }
->>>>>>> main
+ 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:
@@ -4938,28 +4563,13 @@ packages:
resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-<<<<<<< HEAD
- '@jest/diff-sequences@30.3.0':
- resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- '@jest/environment@30.3.0':
- resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==}
- 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: '*'
@@ -4968,92 +4578,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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- '@jest/expect-utils@30.3.0':
- resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- '@jest/expect@30.3.0':
- resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- '@jest/fake-timers@30.3.0':
- resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==}
- 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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- '@jest/globals@30.3.0':
- resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==}
- 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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- '@jest/reporters@30.3.0':
- resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==}
- 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 }
->>>>>>> main
+ 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:
@@ -5068,64 +4630,29 @@ packages:
resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-<<<<<<< HEAD
- '@jest/snapshot-utils@30.3.0':
- resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==}
- 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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- '@jest/test-result@30.3.0':
- resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- '@jest/test-sequencer@30.3.0':
- resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- '@jest/transform@30.3.0':
- resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==}
- 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 }
->>>>>>> main
+ 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==}
@@ -5135,18 +4662,9 @@ packages:
resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-<<<<<<< HEAD
- '@jest/types@30.3.0':
- resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==}
- 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 }
->>>>>>> main
+ 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==}
@@ -5192,20 +4710,11 @@ packages:
resolution: {integrity: sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==}
engines: {node: '>=12'}
-<<<<<<< HEAD
- '@napi-rs/wasm-runtime@0.2.12':
- resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
-
-=======
->>>>>>> main
'@napi-rs/wasm-runtime@0.2.4':
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
@@ -5215,10 +4724,7 @@ packages:
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==}
@@ -5417,296 +4923,125 @@ packages:
'@one-ini/wasm@0.1.1':
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
-<<<<<<< HEAD
- '@oxfmt/binding-android-arm-eabi@0.42.0':
- resolution: {integrity: sha512-dsqPTYsozeokRjlrt/b4E7Pj0z3eS3Eg74TWQuuKbjY4VttBmA88rB7d50Xrd+TZ986qdXCNeZRPEzZHAe+jow==}
+ '@oxfmt/binding-android-arm-eabi@0.51.0':
+ resolution: {integrity: sha512-Ni0sCqg5CIHaLIYFGj+ncbcumylvNC6FE4rfD0KfdmnWHbPJ+zev0qZCXKxy2hFVa0fYRK0yPzf5nzPbkZou7g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
- '@oxfmt/binding-android-arm64@0.42.0':
- resolution: {integrity: sha512-t+aAjHxcr5eOBphFHdg1ouQU9qmZZoRxnX7UOJSaTwSoKsb6TYezNKO0YbWytGXCECObRqNcUxPoPr0KaraAIg==}
+ '@oxfmt/binding-android-arm64@0.51.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.42.0':
- resolution: {integrity: sha512-ulpSEYMKg61C5bRMZinFHrKJYRoKGVbvMEXA5zM1puX3O9T6Q4XXDbft20yrDijpYWeuG59z3Nabt+npeTsM1A==}
+ '@oxfmt/binding-darwin-arm64@0.51.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.42.0':
- resolution: {integrity: sha512-ttxLKhQYPdFiM8I/Ri37cvqChE4Xa562nNOsZFcv1CKTVLeEozXjKuYClNvxkXmNlcF55nzM80P+CQkdFBu+uQ==}
+ '@oxfmt/binding-darwin-x64@0.51.0':
+ resolution: {integrity: sha512-9aUMGmVxdHjYMsEAW1tNRoieTJXlVNDFkRvIR1J7LttJXWjVYCu2ekclLij2KJtxBxSQOYSHd12ME/adVGVbZg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@oxfmt/binding-freebsd-x64@0.42.0':
- resolution: {integrity: sha512-Og7QS3yI3tdIKYZ58SXik0rADxIk2jmd+/YvuHRyKULWpG4V2fR5V4hvKm624Mc0cQET35waPXiCQWvjQEjwYQ==}
+ '@oxfmt/binding-freebsd-x64@0.51.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.42.0':
- resolution: {integrity: sha512-jwLOw/3CW4H6Vxcry4/buQHk7zm9Ne2YsidzTL1kpiMe4qqrRCwev3dkyWe2YkFmP+iZCQ7zku4KwjcLRoh8ew==}
+ '@oxfmt/binding-linux-arm-gnueabihf@0.51.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.42.0':
- resolution: {integrity: sha512-XwXu2vkMtiq2h7tfvN+WA/9/5/1IoGAVCFPiiQUvcAuG3efR97KNcRGM8BetmbYouFotQ2bDal3yyjUx6IPsTg==}
+ '@oxfmt/binding-linux-arm-musleabihf@0.51.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.42.0':
- resolution: {integrity: sha512-ea7s/XUJoT7ENAtUQDudFe3nkSM3e3Qpz4nJFRdzO2wbgXEcjnchKLEsV3+t4ev3r8nWxIYr9NRjPWtnyIFJVA==}
+ '@oxfmt/binding-linux-arm64-gnu@0.51.0':
+ resolution: {integrity: sha512-jOgDzSqWcICGRjsp4mc08FxKMN8vzP2Kgs4E0d2HUP99F+nJDQKklRV4Zuj+0gcBgjrzx2CbpqaIdUVPepCojA==}
engines: {node: ^20.19.0 || >=22.12.0}
-=======
- '@oxfmt/binding-android-arm-eabi@0.51.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 }
cpu: [arm64]
- os: [android]
+ os: [linux]
+ libc: [glibc]
- '@oxfmt/binding-darwin-arm64@0.51.0':
- resolution:
- {
- integrity: sha512-6LsUNIdURhhcIfIn8+xsOb61mSTa9msAHTeSGx9Jf4rsP/gN8PGCF+SKWPAQZbND2w/WBkqQ6303jqEEIXzMdQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
+ '@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}
cpu: [arm64]
- os: [darwin]
-
- '@oxfmt/binding-darwin-x64@0.51.0':
- resolution:
- {
- integrity: sha512-9aUMGmVxdHjYMsEAW1tNRoieTJXlVNDFkRvIR1J7LttJXWjVYCu2ekclLij2KJtxBxSQOYSHd12ME/adVGVbZg==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
- cpu: [x64]
- os: [darwin]
+ os: [linux]
+ libc: [musl]
- '@oxfmt/binding-freebsd-x64@0.51.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 }
- 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 }
- 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 }
->>>>>>> main
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
-<<<<<<< HEAD
- '@oxfmt/binding-linux-arm64-musl@0.42.0':
- resolution: {integrity: sha512-+JA0YMlSdDqmacygGi2REp57c3fN+tzARD8nwsukx9pkCHK+6DkbAA9ojS4lNKsiBjIW8WWa0pBrBWhdZEqfuw==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
- '@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 }
->>>>>>> main
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
-<<<<<<< HEAD
- '@oxfmt/binding-linux-ppc64-gnu@0.42.0':
- resolution: {integrity: sha512-VfnET0j4Y5mdfCzh5gBt0NK28lgn5DKx+8WgSMLYYeSooHhohdbzwAStLki9pNuGy51y4I7IoW8bqwAaCMiJQg==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
'@oxfmt/binding-linux-ppc64-gnu@0.51.0':
- resolution:
- {
- integrity: sha512-NapfjYsABFqTJ1Dn9Efq6sN5esaHconVKwVLbDGNQLrwpOx/g17mkwErHzU72PutL67nf3wNAkbq122H+zLxag==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
->>>>>>> main
+ resolution: {integrity: sha512-NapfjYsABFqTJ1Dn9Efq6sN5esaHconVKwVLbDGNQLrwpOx/g17mkwErHzU72PutL67nf3wNAkbq122H+zLxag==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@oxfmt/binding-linux-riscv64-gnu@0.42.0':
- resolution: {integrity: sha512-gVlCbmBkB0fxBWbhBj9rcxezPydsQHf4MFKeHoTSPicOQ+8oGeTQgQ8EeesSybWeiFPVRx3bgdt4IJnH6nOjAA==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
'@oxfmt/binding-linux-riscv64-gnu@0.51.0':
- resolution:
- {
- integrity: sha512-5dlDt1dUZCVi6elIhiK1PWg9wpTzTcIuj0IZnSurvIoMrhOWqqTcc1dSTxcSkNaBZhfsNqRZdINI1zAgbKkJNQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
->>>>>>> main
+ resolution: {integrity: sha512-5dlDt1dUZCVi6elIhiK1PWg9wpTzTcIuj0IZnSurvIoMrhOWqqTcc1dSTxcSkNaBZhfsNqRZdINI1zAgbKkJNQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@oxfmt/binding-linux-riscv64-musl@0.42.0':
- resolution: {integrity: sha512-zN5OfstL0avgt/IgvRu0zjQzVh/EPkcLzs33E9LMAzpqlLWiPWeMDZyMGFlSRGOdDjuNmlZBCgj0pFnK5u32TQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
'@oxfmt/binding-linux-riscv64-musl@0.51.0':
- resolution:
- {
- integrity: sha512-pgdWUJn0S5nulyiVdlFV8DzCUnGXkU99W5PSkkmbaZW+LrZBPxpezun4G0DDHbQaVYuJeCuKsXsGKGo77CkUTQ==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
->>>>>>> main
+ resolution: {integrity: sha512-pgdWUJn0S5nulyiVdlFV8DzCUnGXkU99W5PSkkmbaZW+LrZBPxpezun4G0DDHbQaVYuJeCuKsXsGKGo77CkUTQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
- '@oxfmt/binding-linux-s390x-gnu@0.42.0':
- resolution: {integrity: sha512-9X6+H2L0qMc2sCAgO9HS03bkGLMKvOFjmEdchaFlany3vNZOjnVui//D8k/xZAtQv2vaCs1reD5KAgPoIU4msA==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
'@oxfmt/binding-linux-s390x-gnu@0.51.0':
- resolution:
- {
- integrity: sha512-2XTFUe97CbDGAI8vjwDfZ1HdakO0XIADyJ24idEg64SC4/K4in/OisXVnrW4NMK7I6TgC7EqRhC0Ln/nKhAemA==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
->>>>>>> main
+ resolution: {integrity: sha512-2XTFUe97CbDGAI8vjwDfZ1HdakO0XIADyJ24idEg64SC4/K4in/OisXVnrW4NMK7I6TgC7EqRhC0Ln/nKhAemA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@oxfmt/binding-linux-x64-gnu@0.42.0':
- resolution: {integrity: sha512-BajxJ6KQvMMdpXGPWhBGyjb2Jvx4uec0w+wi6TJZ6Tv7+MzPwe0pO8g5h1U0jyFgoaF7mDl6yKPW3ykWcbUJRw==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
'@oxfmt/binding-linux-x64-gnu@0.51.0':
- resolution:
- {
- integrity: sha512-kQ1OuCqqt/yyf0ZN9VFxW1/JnlgJgii3Dr7pWf9vNBvrX1hv6g39/+mc5oGRHRGJFZtl3zsGDWR9c5N2B/gwBw==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
->>>>>>> main
+ resolution: {integrity: sha512-kQ1OuCqqt/yyf0ZN9VFxW1/JnlgJgii3Dr7pWf9vNBvrX1hv6g39/+mc5oGRHRGJFZtl3zsGDWR9c5N2B/gwBw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@oxfmt/binding-linux-x64-musl@0.42.0':
- resolution: {integrity: sha512-0wV284I6vc5f0AqAhgAbHU2935B4bVpncPoe5n/WzVZY/KnHgqxC8iSFGeSyLWEgstFboIcWkOPck7tqbdHkzA==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
'@oxfmt/binding-linux-x64-musl@0.51.0':
- resolution:
- {
- integrity: sha512-ARTYqxHF475o96Gbn41hvSWSSRygPlRDXZZgZ9I2scU1y0qiWpCQyZCoefaQa0mwv+wwtZ+luS4YOzsRzM/izg==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
->>>>>>> main
+ resolution: {integrity: sha512-ARTYqxHF475o96Gbn41hvSWSSRygPlRDXZZgZ9I2scU1y0qiWpCQyZCoefaQa0mwv+wwtZ+luS4YOzsRzM/izg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
- '@oxfmt/binding-openharmony-arm64@0.42.0':
- resolution: {integrity: sha512-p4BG6HpGnhfgHk1rzZfyR6zcWkE7iLrWxyehHfXUy4Qa5j3e0roglFOdP/Nj5cJJ58MA3isQ5dlfkW2nNEpolw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [openharmony]
-
- '@oxfmt/binding-win32-arm64-msvc@0.42.0':
- resolution: {integrity: sha512-mn//WV60A+IetORDxYieYGAoQso4KnVRRjORDewMcod4irlRe0OSC7YPhhwaexYNPQz/GCFk+v9iUcZ2W22yxQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [win32]
-
- '@oxfmt/binding-win32-ia32-msvc@0.42.0':
- resolution: {integrity: sha512-3gWltUrvuz4LPJXWivoAxZ28Of2O4N7OGuM5/X3ubPXCEV8hmgECLZzjz7UYvSDUS3grfdccQwmjynm+51EFpw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [ia32]
- os: [win32]
-
- '@oxfmt/binding-win32-x64-msvc@0.42.0':
- resolution: {integrity: sha512-Wg4TMAfQRL9J9AZevJ/ZNy3uyyDztDYQtGr4P8UyyzIhLhFrdSmz1J/9JT+rv0fiCDLaFOBQnj3f3K3+a5PzDQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
'@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 }
->>>>>>> main
+ resolution: {integrity: sha512-73RqdAuVKQTkjZIDw08JaDHUM4lav5Qu+CaPwg4QbbA7k8o7LEW0p3UsfZ/F8dsO/pwVYh3RzFcanwLRTTahbQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
@@ -5753,24 +5088,13 @@ packages:
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==}
-<<<<<<< HEAD
- '@pgsql/utils@17.8.15':
- resolution: {integrity: sha512-Q9szjg1ztXUhyoi49wt1kJvO/H+ohtaKXpkGxVlAlpmxh4/t7AXt1ldQX/LeKrlVqnisHrEKP9XgvR02pq+1oQ==}
-=======
'@pgsql/utils@17.8.17':
- resolution:
- {
- integrity: sha512-JAbRaKBdH0c6GbSgiepwdsis9AG9Kgz9sdmL5e+iBYFZRsFBGKoe1jnxJ0tAMjWYcLus2qeDN6gqQaPYtx1EOg==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-JAbRaKBdH0c6GbSgiepwdsis9AG9Kgz9sdmL5e+iBYFZRsFBGKoe1jnxJ0tAMjWYcLus2qeDN6gqQaPYtx1EOg==}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
@@ -5780,18 +5104,9 @@ packages:
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
-<<<<<<< HEAD
- '@playwright/test@1.58.2':
- resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==}
- engines: {node: '>=18'}
-=======
'@playwright/test@1.60.0':
- resolution:
- {
- integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==,
- }
- engines: { node: '>=18' }
->>>>>>> main
+ resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==}
+ engines: {node: '>=18'}
hasBin: true
'@protobufjs/aspromise@1.1.2':
@@ -5800,16 +5115,8 @@ packages:
'@protobufjs/base64@1.1.2':
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
-<<<<<<< HEAD
- '@protobufjs/codegen@2.0.4':
- resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
-=======
'@protobufjs/codegen@2.0.5':
- resolution:
- {
- integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==}
'@protobufjs/eventemitter@1.1.0':
resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
@@ -5820,16 +5127,8 @@ packages:
'@protobufjs/float@1.0.2':
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
-<<<<<<< HEAD
- '@protobufjs/inquire@1.1.0':
- resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
-=======
'@protobufjs/inquire@1.1.1':
- resolution:
- {
- integrity: sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==}
'@protobufjs/path@1.1.2':
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
@@ -5837,16 +5136,8 @@ packages:
'@protobufjs/pool@1.1.0':
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
-<<<<<<< HEAD
- '@protobufjs/utf8@1.1.0':
- resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
-=======
'@protobufjs/utf8@1.1.1':
- resolution:
- {
- integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==}
'@radix-ui/primitive@1.1.3':
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
@@ -6397,316 +5688,62 @@ packages:
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==}
-<<<<<<< HEAD
- '@sinonjs/fake-timers@15.1.1':
- resolution: {integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==}
-
- '@smithy/abort-controller@4.2.12':
- resolution: {integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/chunked-blob-reader-native@4.2.3':
- resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/chunked-blob-reader@5.2.2':
- resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/config-resolver@4.4.11':
- resolution: {integrity: sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/core@3.23.12':
- resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/credential-provider-imds@4.2.12':
- resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/eventstream-codec@4.2.12':
- resolution: {integrity: sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/eventstream-serde-browser@4.2.12':
- resolution: {integrity: sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/eventstream-serde-config-resolver@4.3.12':
- resolution: {integrity: sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/eventstream-serde-node@4.2.12':
- resolution: {integrity: sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/eventstream-serde-universal@4.2.12':
- resolution: {integrity: sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/fetch-http-handler@5.3.15':
- resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/hash-blob-browser@4.2.13':
- resolution: {integrity: sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/hash-node@4.2.12':
- resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/hash-stream-node@4.2.12':
- resolution: {integrity: sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/invalid-dependency@4.2.12':
- resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==}
- engines: {node: '>=18.0.0'}
-=======
'@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' }
->>>>>>> main
+ 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'}
-<<<<<<< HEAD
- '@smithy/is-array-buffer@4.2.2':
- resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/md5-js@4.2.12':
- resolution: {integrity: sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/middleware-content-length@4.2.12':
- resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/middleware-endpoint@4.4.25':
- resolution: {integrity: sha512-dqjLwZs2eBxIUG6Qtw8/YZ4DvzHGIf0DA18wrgtfP6a50UIO7e2nY0FPdcbv5tVJKqWCCU5BmGMOUwT7Puan+A==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/middleware-endpoint@4.4.27':
- resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/middleware-retry@4.4.42':
- resolution: {integrity: sha512-vbwyqHRIpIZutNXZpLAozakzamcINaRCpEy1MYmK6xBeW3xN+TyPRA123GjXnuxZIjc9848MRRCugVMTXxC4Eg==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/middleware-serde@4.2.15':
- resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/middleware-stack@4.2.12':
- resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/node-config-provider@4.3.12':
- resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==}
- engines: {node: '>=18.0.0'}
-
-=======
->>>>>>> main
'@smithy/node-http-handler@4.4.16':
resolution: {integrity: sha512-ULC8UCS/HivdCB3jhi+kLFYe4B5gxH2gi9vHBfEIiRrT2jfKiZNiETJSlzRtE6B26XbBHjPtc8iZKSNqMol9bw==}
engines: {node: '>=18.0.0'}
-<<<<<<< HEAD
- '@smithy/property-provider@4.2.12':
- resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/protocol-http@5.3.12':
- resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/querystring-builder@4.2.12':
- resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/querystring-parser@4.2.12':
- resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/service-error-classification@4.2.12':
- resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/shared-ini-file-loader@4.4.7':
- resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/signature-v4@5.3.12':
- resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/smithy-client@4.12.5':
- resolution: {integrity: sha512-UqwYawyqSr/aog8mnLnfbPurS0gi4G7IYDcD28cUIBhsvWs1+rQcL2IwkUQ+QZ7dibaoRzhNF99fAQ9AUcO00w==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/smithy-client@4.12.7':
- resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/types@4.13.1':
- resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/url-parser@4.2.12':
- resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-base64@4.3.2':
- resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-body-length-browser@4.2.2':
- resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-body-length-node@4.2.3':
- resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==}
- 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' }
->>>>>>> main
+ 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'}
-<<<<<<< HEAD
- '@smithy/util-buffer-from@4.2.2':
- resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-config-provider@4.2.2':
- resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-defaults-mode-browser@4.3.41':
- resolution: {integrity: sha512-M1w1Ux0rSVvBOxIIiqbxvZvhnjQ+VUjJrugtORE90BbadSTH+jsQL279KRL3Hv0w69rE7EuYkV/4Lepz/NBW9g==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-defaults-mode-node@4.2.44':
- resolution: {integrity: sha512-YPze3/lD1KmWuZsl9JlfhcgGLX7AXhSoaCDtiPntUjNW5/YY0lOHjkcgxyE9x/h5vvS1fzDifMGjzqnNlNiqOQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-endpoints@3.3.3':
- resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-hex-encoding@4.2.2':
- resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-middleware@4.2.12':
- resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-retry@4.2.12':
- resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-stream@4.5.20':
- resolution: {integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-uri-escape@4.2.2':
- resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==}
- engines: {node: '>=18.0.0'}
-
-=======
->>>>>>> main
'@smithy/util-utf8@2.3.0':
resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==}
engines: {node: '>=14.0.0'}
-<<<<<<< HEAD
- '@smithy/util-utf8@4.2.2':
- resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/util-waiter@4.2.13':
- resolution: {integrity: sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==}
- engines: {node: '>=18.0.0'}
-
- '@smithy/uuid@1.1.2':
- resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==}
- engines: {node: '>=18.0.0'}
-
-=======
->>>>>>> main
'@styled-system/background@5.1.2':
resolution: {integrity: sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==}
@@ -6767,11 +5804,8 @@ packages:
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==}
@@ -6789,11 +5823,8 @@ packages:
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
@@ -6826,25 +5857,14 @@ packages:
resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==}
engines: {node: ^16.14.0 || >=18.0.0}
-<<<<<<< HEAD
- '@tybys/wasm-util@0.10.1':
- resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
-=======
'@tybys/wasm-util@0.10.2':
- resolution:
- {
- integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
'@tybys/wasm-util@0.9.0':
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==}
@@ -6853,10 +5873,7 @@ packages:
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==}
@@ -6880,10 +5897,7 @@ packages:
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': '*'
@@ -6917,12 +5931,9 @@ packages:
'@types/http-errors@2.0.5':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
-<<<<<<< HEAD
'@types/interpret@1.1.4':
resolution: {integrity: sha512-r+tPKWHYqaxJOYA3Eik0mMi+SEREqOXLmsooRFmc6GHv7nWUDixFtKN+cegvsPlDcEZd9wxsdp041v2imQuvag==}
-=======
->>>>>>> main
'@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
@@ -6942,10 +5953,7 @@ packages:
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==}
@@ -6977,22 +5985,11 @@ packages:
'@types/node@22.19.15':
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
-<<<<<<< HEAD
- '@types/node@25.5.0':
- resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
-=======
'@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==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==}
'@types/nodemailer@7.0.11':
resolution: {integrity: sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==}
@@ -7003,16 +6000,8 @@ packages:
'@types/pg-copy-streams@1.2.5':
resolution: {integrity: sha512-7D6/GYW2uHIaVU6S/5omI+6RZnwlZBpLQDZAH83xX1rjxAOK0f6/deKyyUTewxqts145VIGn6XWYz1YGf50G5g==}
-<<<<<<< HEAD
- '@types/pg@8.18.0':
- resolution: {integrity: sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==}
-=======
'@types/pg@8.20.0':
- resolution:
- {
- integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==}
'@types/pluralize@0.0.33':
resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==}
@@ -7032,10 +6021,7 @@ packages:
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==}
@@ -7052,16 +6038,8 @@ packages:
'@types/shelljs@0.10.0':
resolution: {integrity: sha512-OEfyhE5Ox+FeoHbhrEDwm0kXxntO6nsyMRCFvNsIBHPZu5rV1w2OjPcLclaC/IZ1TlzZPgbeMfwAZEi5N238yQ==}
-<<<<<<< HEAD
- '@types/smtp-server@3.5.12':
- resolution: {integrity: sha512-IBemrqI6nzvbgwE41Lnd4v4Yf1Kc7F1UHjk1GFBLNhLcI/Zop1ggHQ8g7Y8QYc6jGVgzWQcsa0MBNcGnDY9UGw==}
-=======
'@types/smtp-server@3.5.13':
- resolution:
- {
- integrity: sha512-S3rGl2KbViH+98/CgHipPIWgtAFnjxLsppxIRbgJHNuZL7Y+py+7kZjT7xS+wOmCxYTn4O7IqLqkvekg99MDVQ==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-S3rGl2KbViH+98/CgHipPIWgtAFnjxLsppxIRbgJHNuZL7Y+py+7kZjT7xS+wOmCxYTn4O7IqLqkvekg99MDVQ==}
'@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
@@ -7076,16 +6054,10 @@ packages:
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==}
@@ -7096,434 +6068,185 @@ packages:
'@types/yargs@17.0.35':
resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==}
-<<<<<<< HEAD
- '@typescript-eslint/eslint-plugin@8.57.0':
- resolution: {integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-=======
'@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 }
->>>>>>> main
+ 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'
-<<<<<<< HEAD
- '@typescript-eslint/parser@8.57.0':
- resolution: {integrity: sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.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 }
->>>>>>> main
+ 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'
-<<<<<<< HEAD
- '@typescript-eslint/project-service@8.57.0':
- resolution: {integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.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 }
->>>>>>> main
+ 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'
-<<<<<<< HEAD
- '@typescript-eslint/scope-manager@8.57.0':
- resolution: {integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@typescript-eslint/tsconfig-utils@8.57.0':
- resolution: {integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.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 }
->>>>>>> main
+ 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'
-<<<<<<< HEAD
- '@typescript-eslint/type-utils@8.57.0':
- resolution: {integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.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 }
->>>>>>> main
+ 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'
-<<<<<<< HEAD
- '@typescript-eslint/types@8.57.0':
- resolution: {integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@typescript-eslint/typescript-estree@8.57.0':
- resolution: {integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.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 }
->>>>>>> main
+ 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'
-<<<<<<< HEAD
- '@typescript-eslint/utils@8.57.0':
- resolution: {integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.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 }
->>>>>>> main
+ 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'
-<<<<<<< HEAD
- '@typescript-eslint/visitor-keys@8.57.0':
- resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@ungap/structured-clone@1.3.0':
- resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
-
- '@unrs/resolver-binding-android-arm-eabi@1.11.1':
- resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
- cpu: [arm]
- os: [android]
-
- '@unrs/resolver-binding-android-arm64@1.11.1':
- resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==}
- cpu: [arm64]
- os: [android]
-
- '@unrs/resolver-binding-darwin-arm64@1.11.1':
- resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==}
- cpu: [arm64]
- os: [darwin]
-
- '@unrs/resolver-binding-darwin-x64@1.11.1':
- resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==}
- cpu: [x64]
- os: [darwin]
-
- '@unrs/resolver-binding-freebsd-x64@1.11.1':
- resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==}
- cpu: [x64]
- os: [freebsd]
-
- '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
- resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==}
- cpu: [arm]
- os: [linux]
-
- '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
- resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==}
- cpu: [arm]
- os: [linux]
-
- '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
- resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
-=======
'@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==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==}
cpu: [arm64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
- resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
-=======
'@unrs/resolver-binding-linux-arm64-musl@1.12.2':
- resolution:
- {
- integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==}
cpu: [arm64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
- '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
- resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
-=======
'@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==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
- resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
-=======
'@unrs/resolver-binding-linux-riscv64-gnu@1.12.2':
- resolution:
- {
- integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
- resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
-=======
'@unrs/resolver-binding-linux-riscv64-musl@1.12.2':
- resolution:
- {
- integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==}
cpu: [riscv64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
- '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
- resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
-=======
'@unrs/resolver-binding-linux-s390x-gnu@1.12.2':
- resolution:
- {
- integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==}
cpu: [s390x]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
- resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
-=======
'@unrs/resolver-binding-linux-x64-gnu@1.12.2':
- resolution:
- {
- integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==}
cpu: [x64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
- '@unrs/resolver-binding-linux-x64-musl@1.11.1':
- resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
-=======
'@unrs/resolver-binding-linux-x64-musl@1.12.2':
- resolution:
- {
- integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==}
cpu: [x64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
- '@unrs/resolver-binding-wasm32-wasi@1.11.1':
- resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
- engines: {node: '>=14.0.0'}
- cpu: [wasm32]
-
- '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
- resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==}
- cpu: [arm64]
- os: [win32]
-
- '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
- resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==}
- cpu: [ia32]
- os: [win32]
-
- '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
- resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==}
-=======
'@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==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==}
cpu: [x64]
os: [win32]
@@ -7553,10 +6276,7 @@ packages:
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==}
@@ -7590,16 +6310,8 @@ packages:
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
-<<<<<<< HEAD
- ajv@8.18.0:
- resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
-=======
ajv@8.20.0:
- resolution:
- {
- integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
@@ -7660,10 +6372,7 @@ packages:
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==}
@@ -7704,18 +6413,9 @@ packages:
axios@1.13.2:
resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
-<<<<<<< HEAD
- babel-jest@30.3.0:
- resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-=======
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 }
->>>>>>> main
+ 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
@@ -7723,18 +6423,9 @@ packages:
resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==}
engines: {node: '>=12'}
-<<<<<<< HEAD
- babel-plugin-jest-hoist@30.3.0:
- resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-=======
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 }
->>>>>>> main
+ 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==}
@@ -7746,18 +6437,9 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0 || ^8.0.0-0
-<<<<<<< HEAD
- babel-preset-jest@30.3.0:
- resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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 }
->>>>>>> main
+ 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
@@ -7816,21 +6498,15 @@ packages:
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}
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==}
@@ -8135,17 +6811,11 @@ packages:
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==}
@@ -8192,12 +6862,6 @@ packages:
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
engines: {node: '>=12.0.0'}
-<<<<<<< HEAD
- cross-fetch@4.1.0:
- resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==}
-
-=======
->>>>>>> main
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -8234,11 +6898,8 @@ packages:
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==}
@@ -8246,18 +6907,9 @@ packages:
csv-parse@6.2.1:
resolution: {integrity: sha512-LRLMV+UCyfMokp8Wb411duBf1gaBKJfOfBWU9eHMJ+b+cJYZsNu3AFmjJf3+yPGd59Exz1TsMjaSFyxnYB9+IQ==}
-<<<<<<< HEAD
- csv-parser@3.2.0:
- resolution: {integrity: sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==}
- engines: {node: '>= 10'}
-=======
csv-parser@3.2.1:
- resolution:
- {
- integrity: sha512-v8RPMSglouR9od735SnwSxLBbCJqEPSbgm1R5qfr8yIiMUCEFjox56kRZid0SvgHJEkxeIEu3+a9QS3YRh7CuA==,
- }
- engines: { node: '>= 10' }
->>>>>>> main
+ resolution: {integrity: sha512-v8RPMSglouR9od735SnwSxLBbCJqEPSbgm1R5qfr8yIiMUCEFjox56kRZid0SvgHJEkxeIEu3+a9QS3YRh7CuA==}
+ engines: {node: '>= 10'}
hasBin: true
dargs@7.0.0:
@@ -8265,11 +6917,8 @@ packages:
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==}
@@ -8295,10 +6944,7 @@ packages:
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==}
@@ -8350,11 +6996,8 @@ packages:
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==}
@@ -8445,16 +7088,8 @@ packages:
resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==}
engines: {node: '>=12'}
-<<<<<<< HEAD
- drizzle-orm@0.45.1:
- resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==}
-=======
drizzle-orm@0.45.2:
- resolution:
- {
- integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==}
peerDependencies:
'@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=4'
@@ -8781,18 +7416,9 @@ packages:
resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-<<<<<<< HEAD
- expect@30.3.0:
- resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==}
- 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 }
->>>>>>> main
+ 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==}
@@ -8817,34 +7443,14 @@ packages:
fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
-<<<<<<< HEAD
- fast-uri@3.1.0:
- resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
-
- fast-xml-builder@1.1.4:
- resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==}
-
- fast-xml-parser@5.5.8:
- resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==}
-=======
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==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==}
hasBin: true
fastq@1.20.1:
@@ -8992,16 +7598,8 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
-<<<<<<< HEAD
- genomic@5.3.9:
- resolution: {integrity: sha512-zPJ+qH993GtbtsQSCd283ErOLF7/4cg5+DGVYHK9laiX8lITF4x0DJMktxAHqgmMdI3QjLuMOcGnTGOH950sFg==}
-=======
genomic@5.6.0:
- resolution:
- {
- integrity: sha512-5VimMClR8yfML+kxmaFAYgPbGqIJ9fhQduNv2V0vnCj+8BqjOyfOnX21jvp0GIkKQSyUZh0YUO5ewMKQqbUEfg==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-5VimMClR8yfML+kxmaFAYgPbGqIJ9fhQduNv2V0vnCj+8BqjOyfOnX21jvp0GIkKQSyUZh0YUO5ewMKQqbUEfg==}
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
@@ -9119,18 +7717,9 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
-<<<<<<< HEAD
- grafast@1.0.0:
- resolution: {integrity: sha512-V4AhdcQhgDDqKZS708WWu8iC6Jd80gVca6zC1M8YUb8gZOOS4r0f/V89KbGFWh0nuLaZQeFj+LZ9Ps9B8F2LEA==}
- engines: {node: '>=22'}
-=======
grafast@1.0.2:
- resolution:
- {
- integrity: sha512-E3PH6hfOfhlJxMmiBplWs3KDmJTIQr/iZ9hs4c73k3pLk9Tx2nSdVNBVIe7D1stejgPbyC6wRxLfXtJmDx2P7A==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ resolution: {integrity: sha512-E3PH6hfOfhlJxMmiBplWs3KDmJTIQr/iZ9hs4c73k3pLk9Tx2nSdVNBVIe7D1stejgPbyC6wRxLfXtJmDx2P7A==}
+ engines: {node: '>=22'}
peerDependencies:
'@envelop/core': ^5.0.0
graphql: 16.13.0
@@ -9162,18 +7751,9 @@ packages:
ws:
optional: true
-<<<<<<< HEAD
- graphile-build-pg@5.0.0:
- resolution: {integrity: sha512-a0FAR5n8UIYMAI1URIuWAAb+dZUDNrP09rYdg7veBUhJQyBtfkUHGNwZ+gDEhRBOvr/eRrw5Jkqc+Moi+XwW8A==}
- engines: {node: '>=22'}
-=======
graphile-build-pg@5.0.2:
- resolution:
- {
- integrity: sha512-8vkg1x5UcooFogPCp+3EUt6qqWX8NG01gANoVFUk9yK2c8DW+aQaF37LAaa96r9JR4FE0yeO2zk6WvOOemk2dg==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ resolution: {integrity: sha512-8vkg1x5UcooFogPCp+3EUt6qqWX8NG01gANoVFUk9yK2c8DW+aQaF37LAaa96r9JR4FE0yeO2zk6WvOOemk2dg==}
+ engines: {node: '>=22'}
peerDependencies:
'@dataplan/pg': ^1.0.0-rc.7
grafast: ^1.0.0-rc.8
@@ -9187,35 +7767,21 @@ packages:
pg:
optional: true
-<<<<<<< HEAD
- graphile-build@5.0.0:
- resolution: {integrity: sha512-hGieff6/UaikT7ywWv2XTFa1mGJ1Zdytqbfw0bmVlXWMOeJGpvCdx9+k5Kpw7aIZ92twPa5yb2HUo0Q8j2Kwzw==}
- engines: {node: '>=22'}
-=======
graphile-build@5.0.2:
- resolution:
- {
- integrity: sha512-ELhDDZ2Y3ZWmF+ZziEd9ytMxFnmBoCX4/1My2Jhifr1FUD9IzWFhzH2qAWKmTvDrIy7b6jc4SV23lWmTbiD3ZA==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ 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
-<<<<<<< HEAD
graphile-config@1.0.0:
resolution: {integrity: sha512-nPKrrpmYT/cMibqHnNL+zLIRrC/SQhop7yV4tZiyrC/C0mckdlghRWR9oPV7UppkeFIdgTt5/7UQCrwhX82faQ==}
engines: {node: '>=22'}
-=======
+
graphile-config@1.0.1:
- resolution:
- {
- integrity: sha512-sVdSWNmetW/WZKVQ0Dii2kCu0Le6X6qwuBRecWg575iOMjbZgxo+b4oVeSOTtR6NTKuLsMYQBkzSaeoAKOPB+A==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ resolution: {integrity: sha512-sVdSWNmetW/WZKVQ0Dii2kCu0Le6X6qwuBRecWg575iOMjbZgxo+b4oVeSOTtR6NTKuLsMYQBkzSaeoAKOPB+A==}
+ engines: {node: '>=22'}
graphile-utils@5.0.0:
resolution: {integrity: sha512-W/qzi7o6w4cakNqapeGNSGmYq0QowsiT0okPdmdUmZe117XiGPACzL+H0vqG0ZqkLUjZ5vNTIOGYQVqJ4Tg6KA==}
@@ -9233,11 +7799,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
@@ -9280,18 +7843,9 @@ packages:
peerDependencies:
graphql: 16.13.0
-<<<<<<< HEAD
- graphql-ws@6.0.7:
- resolution: {integrity: sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg==}
- engines: {node: '>=20'}
-=======
graphql-ws@6.0.8:
- resolution:
- {
- integrity: sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw==,
- }
- engines: { node: '>=20' }
->>>>>>> main
+ resolution: {integrity: sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw==}
+ engines: {node: '>=20'}
peerDependencies:
'@fastify/websocket': ^10 || ^11
crossws: ~0.3
@@ -9309,18 +7863,9 @@ packages:
resolution: {integrity: sha512-uSisMYERbaB9bkA9M4/4dnqyktaEkf1kMHNKq/7DHyxVeWqHQ2mBmVqm5u6/FVHwF3iCNalKcg82Zfl+tffWoA==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
-<<<<<<< HEAD
- handlebars@4.7.8:
- resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
- engines: {node: '>=0.4.7'}
-=======
handlebars@4.7.9:
- resolution:
- {
- integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==,
- }
- engines: { node: '>=0.4.7' }
->>>>>>> main
+ resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==}
+ engines: {node: '>=0.4.7'}
hasBin: true
hard-rejection@2.1.0:
@@ -9369,11 +7914,8 @@ packages:
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==}
@@ -9496,16 +8038,8 @@ packages:
resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==}
engines: {node: '>=12.0.0'}
-<<<<<<< HEAD
- inquirerer@4.7.0:
- resolution: {integrity: sha512-Gp6Bd7NGeA1y/vV+q0Dl7KqakMHvHiTXyRIMYXCH4L6tQ3AWJBWd6BRmxikOp9t1C8eoJIGrHs7iJt34hx573Q==}
-=======
inquirerer@4.8.1:
- resolution:
- {
- integrity: sha512-X8cPy91JMH6EmUPUqgnxc+oYssHdQlitWR23youH2208F2enxElCKc6Mt/5H8KAupYDgOuRuyBO+SRaRXStj8A==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-X8cPy91JMH6EmUPUqgnxc+oYssHdQlitWR23youH2208F2enxElCKc6Mt/5H8KAupYDgOuRuyBO+SRaRXStj8A==}
interpret@3.1.1:
resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==}
@@ -9582,10 +8116,7 @@ packages:
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==}
@@ -9669,40 +8200,17 @@ packages:
engines: {node: '>=10'}
hasBin: true
-<<<<<<< HEAD
- jest-changed-files@30.3.0:
- resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-circus@30.3.0:
- resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-cli@30.3.0:
- resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-=======
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 }
->>>>>>> main
+ 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
@@ -9710,18 +8218,9 @@ packages:
node-notifier:
optional: true
-<<<<<<< HEAD
- jest-config@30.3.0:
- resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-=======
jest-config@30.4.2:
- resolution:
- {
- integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==,
- }
- engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
->>>>>>> main
+ 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'
@@ -9742,50 +8241,21 @@ packages:
resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-<<<<<<< HEAD
- jest-diff@30.3.0:
- resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-docblock@30.2.0:
- resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-each@30.3.0:
- resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-environment-node@30.3.0:
- resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==}
- 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:
@@ -9793,97 +8263,48 @@ 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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- jest-haste-map@30.3.0:
- resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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 }
->>>>>>> main
+ 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'}
-<<<<<<< HEAD
- jest-leak-detector@30.3.0:
- resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-=======
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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- jest-matcher-utils@30.3.0:
- resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==}
- 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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- jest-message-util@30.3.0:
- resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==}
- 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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- jest-mock@30.3.0:
- resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==}
- 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 }
->>>>>>> main
+ 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==}
@@ -9898,130 +8319,53 @@ packages:
resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-<<<<<<< HEAD
- jest-resolve-dependencies@30.3.0:
- resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-resolve@30.3.0:
- resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-runner@30.3.0:
- resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-runtime@30.3.0:
- resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-snapshot@30.3.0:
- resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==}
- 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 }
->>>>>>> main
+ 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}
-<<<<<<< HEAD
- jest-util@30.3.0:
- resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-validate@30.3.0:
- resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-watcher@30.3.0:
- resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest-worker@30.3.0:
- resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==}
- engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
-
- jest@30.3.0:
- resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==}
- 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 }
->>>>>>> main
+ 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
@@ -10029,16 +8373,8 @@ packages:
node-notifier:
optional: true
-<<<<<<< HEAD
- jiti@2.6.1:
- resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
-=======
jiti@2.7.0:
- resolution:
- {
- integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true
js-beautify@1.15.4:
@@ -10069,11 +8405,8 @@ packages:
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:
@@ -10249,16 +8582,8 @@ packages:
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-<<<<<<< HEAD
- lodash@4.17.23:
- resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
-=======
lodash@4.18.1:
- resolution:
- {
- integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
@@ -10303,13 +8628,6 @@ packages:
resolution: {integrity: sha512-YrdaZEAJwwjXGBTfZTNQ1LM7tmkdUaz2NpZEu7+zULcG4Wrlhd7cWSNZW0bxT3bP48k5N0mZWz8C2f9gc2+Geg==}
engines: {node: '>=18.0.0'}
-<<<<<<< HEAD
- makage@0.1.12:
- resolution: {integrity: sha512-R3bITl50Ts2GzoaErywe8n24Iu2qbvbNOqOyidjDjh6iqK0CAj2VzIk3xRS4z8Q4xDQzaJrcb2+dGDjqRj6ChA==}
- hasBin: true
-
-=======
->>>>>>> main
makage@0.3.0:
resolution: {integrity: sha512-HiBTpSy6iLnI+apv91QTWgBlFiePOCAUXmdYwlVzqkfwtUc0aM1FNu70CLs8xvc2u/R43m3J1SuWprMcKpV+vw==}
hasBin: true
@@ -10434,11 +8752,8 @@ packages:
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==}
@@ -10696,22 +9011,8 @@ packages:
neo-async@2.6.2:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
-<<<<<<< HEAD
- nested-obj@0.1.10:
- resolution: {integrity: sha512-5V2kUPrBee/tmoS2p0IJ35BcaJuW1p1yXF5GP8JpXIkDoPbaYeYypAHizUeZkAUxcC7Rago7izWmEq7qa8+Mhw==}
-
- nested-obj@0.1.5:
- resolution: {integrity: sha512-04Y7qDMlI8RbYTn0cJAKaw/mLrO9UmLj3xbrjTZKDfOn9f3b/RXEQFIIpveJlwn8KfPwdVFWLZUaL5gNuQ7G0w==}
-
- nested-obj@0.2.1:
- resolution: {integrity: sha512-MQnXdT8qoxxu5/ONQ8tO70HsuvuUAhLmAvOr1RaAtWqpGda+JycVIhN1Pclq5Zny7sr4Jn4wKgIR8IpdnXU+EQ==}
-=======
nested-obj@0.2.2:
- resolution:
- {
- integrity: sha512-M1etu+T6Ai9Bo06L3K3nWD0ytZWltggBGsrxJlOGvMNGlCA4fokUVlbPKoWzsiiRX+PXq6Cb1xFEn4chiyC7MQ==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-M1etu+T6Ai9Bo06L3K3nWD0ytZWltggBGsrxJlOGvMNGlCA4fokUVlbPKoWzsiiRX+PXq6Cb1xFEn4chiyC7MQ==}
no-case@2.3.2:
resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==}
@@ -10756,18 +9057,9 @@ packages:
resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==}
engines: {node: '>=6.0.0'}
-<<<<<<< HEAD
- nodemailer@7.0.13:
- resolution: {integrity: sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==}
- engines: {node: '>=6.0.0'}
-=======
nodemailer@8.0.5:
- resolution:
- {
- integrity: sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==,
- }
- engines: { node: '>=6.0.0' }
->>>>>>> main
+ resolution: {integrity: sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==}
+ engines: {node: '>=6.0.0'}
nodemon@3.1.14:
resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==}
@@ -10839,10 +9131,7 @@ packages:
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==}
@@ -10898,18 +9187,9 @@ packages:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
-<<<<<<< HEAD
- oxfmt@0.42.0:
- resolution: {integrity: sha512-QhejGErLSMReNuZ6vxgFHDyGoPbjTRNi6uGHjy0cvIjOQFqD6xmr/T+3L41ixR3NIgzcNiJ6ylQKpvShTgDfqg==}
- engines: {node: ^20.19.0 || >=22.12.0}
-=======
oxfmt@0.51.0:
- resolution:
- {
- integrity: sha512-l/AoAnaEOV7Q5/Z9kHOMDehVJnCgYN7wRoooWCTUMBMi16BJhLZqd9cmCnwcVFfVlzkt53zK2KLPFNp8vSsoDg==,
- }
- engines: { node: ^20.19.0 || >=22.12.0 }
->>>>>>> main
+ resolution: {integrity: sha512-l/AoAnaEOV7Q5/Z9kHOMDehVJnCgYN7wRoooWCTUMBMi16BJhLZqd9cmCnwcVFfVlzkt53zK2KLPFNp8vSsoDg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
svelte: ^5.0.0
@@ -11041,18 +9321,9 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
-<<<<<<< HEAD
- path-expression-matcher@1.2.0:
- resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==}
- engines: {node: '>=14.0.0'}
-=======
path-expression-matcher@1.5.0:
- resolution:
- {
- integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==,
- }
- engines: { node: '>=14.0.0' }
->>>>>>> main
+ resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==}
+ engines: {node: '>=14.0.0'}
path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
@@ -11084,19 +9355,13 @@ packages:
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==}
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==}
@@ -11105,64 +9370,32 @@ packages:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
-<<<<<<< HEAD
- pg-introspection@1.0.0:
- resolution: {integrity: sha512-5Q+wTTbPO0+yVWwC8Qh58b12HOqXgE6Si+Xq17+QBngpJ2u6yZvhRxHlrSGimqAMWLIXkV6/FvCTqwcQ1IBvAw==}
- engines: {node: '>=22'}
-=======
pg-introspection@1.0.1:
- resolution:
- {
- integrity: sha512-HwxpCEWygpRPfvFf7IVtEPchtjl1Fw0TxzCYXJIQdTEFio/AcGnp2XI5x+LpowbyEa3XgB9L5gvw2D0Jqji4eA==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ resolution: {integrity: sha512-HwxpCEWygpRPfvFf7IVtEPchtjl1Fw0TxzCYXJIQdTEFio/AcGnp2XI5x+LpowbyEa3XgB9L5gvw2D0Jqji4eA==}
+ engines: {node: '>=22'}
pg-pool@3.13.0:
resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==}
peerDependencies:
pg: '>=8.0'
-<<<<<<< HEAD
- pg-proto-parser@1.30.5:
- resolution: {integrity: sha512-DWXRF5u3hcAwJfrlfKjqOfSex2E6d4Lh9Y3NxFsaieQD1ZoQlsceAn1Xp6C5HRKnHxUB/F5N7R4aVeDBG/sk4Q==}
-=======
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==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-2XwPyl9oz5Pest4ebaovRTTJN8MXaa/XvqMQzKq127fFcl4I1POUgV/FtzHzg/p8FjtO5yHsipeW/kAumzNxxw==}
pg-protocol@1.13.0:
resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==}
-<<<<<<< HEAD
- pg-sql2@5.0.0:
- resolution: {integrity: sha512-gvmfl0XeOeFjd+1aH5uIp1eZxUM6LmaMP8yy1EWE16XaPeUP8dhKdHtdHc0MsX0ZgCy+1g67yS1HCYWns2TdmQ==}
- engines: {node: '>=22'}
-=======
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' }
->>>>>>> main
+ resolution: {integrity: sha512-DdOZNUBhuBuGcq3UgUNsYE9FPdPzw9YA1K/WQxutNhC17DA/CImapyamv6lliVvdAVNZ+CWB1z+p7SmSJmkezw==}
+ engines: {node: '>=22'}
pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
@@ -11178,11 +9411,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:
@@ -11192,25 +9422,11 @@ packages:
pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
-<<<<<<< HEAD
- pgsql-deparser@17.18.2:
- resolution: {integrity: sha512-mnoT6ti7IFLwSUxe0UkxMjfUtyyWmwClf2sJyLbbRGbZ53SgiT493INFyxmeRvQxc99lmpz6aCxUnjj0ZhGlJA==}
-
- pgsql-parser@17.9.14:
- resolution: {integrity: sha512-2qhO2DXkIbqtRdXN4dj8dD/RmSRtNfWxK08dYQ630WwJe1AF6ExuDV0zYGN8BR2NhCHqWmU3qKqJJPBXdib5DQ==}
-=======
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==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-6+k0EtTn0CEQTR5v2APARu9En4vm46TpmpdMSfKDHkZwWZuEc08B7SeVg32VxNQ2HD5xk+dQ9TD0k9m+S+vFgg==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -11228,11 +9444,8 @@ packages:
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==}
@@ -11258,31 +9471,14 @@ packages:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
-<<<<<<< HEAD
- playwright-core@1.58.2:
- resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==}
- engines: {node: '>=18'}
- hasBin: true
-
- playwright@1.58.2:
- resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==}
- engines: {node: '>=18'}
-=======
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' }
->>>>>>> main
+ resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==}
+ engines: {node: '>=18'}
hasBin: true
pluralize@7.0.0:
@@ -11300,18 +9496,30 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
-<<<<<<< HEAD
postgraphile@5.0.0:
resolution: {integrity: sha512-BEv+qrOQBgMtzq3xWhHzwD9+cTdFz8XG/x7e4qWd/4+oqIDrKzOX+OXhFW8vx7jeRnvCMQ3Y8DYe/4tS4DJjqA==}
engines: {node: '>=22'}
-=======
+ hasBin: true
+ peerDependencies:
+ '@dataplan/json': 1.0.0
+ '@dataplan/pg': ^1.0.0
+ '@envelop/core': ^5.0.0
+ grafast: ^1.0.0
+ grafserv: ^1.0.0
+ graphile-build: ^5.0.0
+ graphile-build-pg: ^5.0.0
+ graphile-config: ^1.0.0
+ graphql: 16.13.0
+ pg: ^8.7.1
+ pg-sql2: ^5.0.0
+ tamedevil: ^0.1.0
+ peerDependenciesMeta:
+ '@envelop/core':
+ optional: true
+
postgraphile@5.0.3:
- resolution:
- {
- integrity: sha512-gO8xOusrIykV65V1rmkqnUz3ZjDU+1y09uWruUshP+t811JFsEZsE7d3UJNjAxgrBcndU0CzgmEWCbvbD3kehA==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ resolution: {integrity: sha512-gO8xOusrIykV65V1rmkqnUz3ZjDU+1y09uWruUshP+t811JFsEZsE7d3UJNjAxgrBcndU0CzgmEWCbvbD3kehA==}
+ engines: {node: '>=22'}
hasBin: true
peerDependencies:
'@dataplan/json': 1.0.0
@@ -11367,11 +9575,8 @@ packages:
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==}
@@ -11381,18 +9586,9 @@ packages:
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}
-<<<<<<< HEAD
- pretty-format@30.3.0:
- resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==}
- 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 }
->>>>>>> main
+ 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==}
@@ -11488,10 +9684,7 @@ packages:
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
@@ -11504,16 +9697,8 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
-<<<<<<< HEAD
- react-is@19.2.4:
- resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==}
-=======
react-is@19.2.6:
- resolution:
- {
- integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==}
react-query@3.39.3:
resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==}
@@ -11561,16 +9746,8 @@ packages:
'@types/react':
optional: true
-<<<<<<< HEAD
- react-test-renderer@19.2.4:
- resolution: {integrity: sha512-Ttl5D7Rnmi6JGMUpri4UjB4BAN0FPs4yRDnu2XSsigCWOLm11o8GwRlVsh27ER+4WFqsGtrBuuv5zumUaRCmKw==}
-=======
react-test-renderer@19.2.6:
- resolution:
- {
- integrity: sha512-GbS6V23YduFTPiWJ5xICbKEjRcqx1Z90js/V5miqhz7qp/d6xSe9Dd6NjSQODFRdzdsqRMPW82E/sFpPRbY5Mw==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-GbS6V23YduFTPiWJ5xICbKEjRcqx1Z90js/V5miqhz7qp/d6xSe9Dd6NjSQODFRdzdsqRMPW82E/sFpPRbY5Mw==}
peerDependencies:
react: ^19.2.6
@@ -11579,11 +9756,8 @@ packages:
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==}
@@ -11736,10 +9910,7 @@ packages:
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==}
@@ -11789,11 +9960,8 @@ packages:
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==}
@@ -11820,11 +9988,8 @@ packages:
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:
@@ -11906,18 +10071,9 @@ packages:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
-<<<<<<< HEAD
- smtp-server@3.18.1:
- resolution: {integrity: sha512-zlUXA6n3HkO0jMyNNc2S67uw7DWHOoLU9vjPo5oW2c8ehJMpRlSumyw4riuvfWPfW/8mryd7ED5PVf4YVg8Y6w==}
- engines: {node: '>=18.18.0'}
-=======
smtp-server@3.18.4:
- resolution:
- {
- integrity: sha512-9EnXPG4Tv+2P/TSEUdFTduYn9IxtxNRsOq/ryVj8ZlT+6MU2um9gn2Td2hHlgH1n+saagMWtici3hn5J5PhU+g==,
- }
- engines: { node: '>=18.18.0' }
->>>>>>> main
+ 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==}
@@ -12005,16 +10161,8 @@ packages:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
-<<<<<<< HEAD
- strfy-js@3.2.1:
- resolution: {integrity: sha512-HSw2lkUJVPZ75I+E3HM7UqHMKvBCwjRt1MIAxPPNtLFjuqCrnDVKQQGfotdj/3qHxuhB6NDQ1rYmNjVpPBiNEA==}
-=======
strfy-js@3.2.2:
- resolution:
- {
- integrity: sha512-hUgJ5k2PR1ivhq4uObxnin5j6GcOr0Y0N1lzi3z6SRhxNqu4rzpDfyoC2ToUAyM8yXNXM0zs6f4KIiqj8NqheQ==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-hUgJ5k2PR1ivhq4uObxnin5j6GcOr0Y0N1lzi3z6SRhxNqu4rzpDfyoC2ToUAyM8yXNXM0zs6f4KIiqj8NqheQ==}
string-length@4.0.2:
resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
@@ -12065,16 +10213,8 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
-<<<<<<< HEAD
- strnum@2.2.0:
- resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==}
-=======
strnum@2.3.0:
- resolution:
- {
- integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==}
styled-components@5.3.11:
resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==}
@@ -12112,10 +10252,7 @@ packages:
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==}
@@ -12124,18 +10261,9 @@ packages:
tabbable@6.4.0:
resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
-<<<<<<< HEAD
- tamedevil@0.1.0:
- resolution: {integrity: sha512-Ry2HVNPnFW6yzNALT+LuABIg2YiTf9orzSl2tCh2mfxLIl0LrnAyadmFDfANdQFzPbPW3Y1DY03QwDoCqJuc/A==}
- engines: {node: '>=22'}
-=======
tamedevil@0.1.1:
- resolution:
- {
- integrity: sha512-YH5/T/FXUYrsfFSsCdLqJwUGAlbTBrK2V78dftXnOIgnOnM9aYBi3C+uUg9pevezjE2ENPyOxHqnXJrTG9WPFQ==,
- }
- engines: { node: '>=22' }
->>>>>>> main
+ resolution: {integrity: sha512-YH5/T/FXUYrsfFSsCdLqJwUGAlbTBrK2V78dftXnOIgnOnM9aYBi3C+uUg9pevezjE2ENPyOxHqnXJrTG9WPFQ==}
+ engines: {node: '>=22'}
tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
@@ -12173,27 +10301,18 @@ packages:
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}
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:
@@ -12216,21 +10335,15 @@ packages:
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==}
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==}
@@ -12245,33 +10358,15 @@ packages:
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
engines: {node: '>=8'}
-<<<<<<< HEAD
- ts-api-utils@2.4.0:
- resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
- engines: {node: '>=18.12'}
- peerDependencies:
- typescript: '>=4.8.4'
-
- ts-jest@29.4.6:
- resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==}
- engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
-=======
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 }
->>>>>>> main
+ 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'
@@ -12393,34 +10488,16 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
-<<<<<<< HEAD
- undici-types@7.18.2:
- resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
-
- undici@7.24.6:
- resolution: {integrity: sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==}
- engines: {node: '>=20.18.1'}
-=======
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' }
->>>>>>> main
+ 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==}
@@ -12444,16 +10521,8 @@ packages:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
-<<<<<<< HEAD
- unrs-resolver@1.11.1:
- resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
-=======
unrs-resolver@1.12.2:
- resolution:
- {
- integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==,
- }
->>>>>>> main
+ resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==}
untildify@4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
@@ -12507,15 +10576,8 @@ packages:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
uuid@10.0.0:
-<<<<<<< HEAD
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).
->>>>>>> main
hasBin: true
v8-compile-cache-lib@3.0.1:
@@ -12584,11 +10646,8 @@ packages:
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==}
@@ -12610,11 +10669,8 @@ packages:
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==}
@@ -12626,11 +10682,8 @@ packages:
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==}
@@ -12688,18 +10741,9 @@ packages:
resolution: {integrity: sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==}
engines: {node: '>=8'}
-<<<<<<< HEAD
- ws@8.19.0:
- resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
- engines: {node: '>=10.0.0'}
-=======
ws@8.20.1:
- resolution:
- {
- integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==,
- }
- engines: { node: '>=10.0.0' }
->>>>>>> main
+ 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'
@@ -12710,24 +10754,15 @@ 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==}
@@ -12746,18 +10781,9 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
-<<<<<<< HEAD
- yaml@2.8.2:
- resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
- engines: {node: '>= 14.6'}
-=======
yaml@2.8.4:
- resolution:
- {
- integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==,
- }
- engines: { node: '>= 14.6' }
->>>>>>> main
+ resolution: {integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==}
+ engines: {node: '>= 14.6'}
hasBin: true
yanse@0.2.1:
@@ -13111,12 +11137,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
@@ -13347,7 +11367,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
@@ -13365,7 +11385,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
@@ -13433,6 +11453,26 @@ snapshots:
grafast: 1.0.2(graphql@16.13.0)
tslib: 2.8.1
+ '@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)':
+ dependencies:
+ '@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
+ '@graphile/lru': 5.0.0
+ '@types/node': 22.19.19
+ chalk: 4.1.2
+ debug: 4.4.3(supports-color@5.5.0)
+ eventemitter3: 5.0.4
+ grafast: 1.0.2(graphql@16.13.0)
+ graphile-config: 1.0.0
+ graphql: 16.13.0
+ pg-sql2: 5.0.1
+ postgres-array: 3.0.4
+ postgres-range: 1.1.4
+ tslib: 2.8.1
+ optionalDependencies:
+ pg: 8.21.0
+ transitivePeerDependencies:
+ - supports-color
+
'@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)':
dependencies:
'@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
@@ -15545,7 +13585,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
@@ -15693,6 +13733,10 @@ snapshots:
'@types/http-errors@2.0.5': {}
+ '@types/interpret@1.1.4':
+ dependencies:
+ '@types/node': 22.19.19
+
'@types/istanbul-lib-coverage@2.0.6': {}
'@types/istanbul-lib-report@3.0.3':
@@ -17636,6 +15680,31 @@ snapshots:
- supports-color
- use-sync-external-store
+ grafserv@1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1):
+ dependencies:
+ '@graphile/lru': 5.0.0
+ debug: 4.4.3(supports-color@5.5.0)
+ eventemitter3: 5.0.4
+ grafast: 1.0.2(graphql@16.13.0)
+ graphile-config: 1.0.0
+ graphql: 16.13.0
+ graphql-ws: 6.0.8(graphql@16.13.0)(ws@8.20.1)
+ ruru: 2.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(debug@4.4.3)(graphile-config@1.0.0)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))
+ tslib: 2.8.1
+ optionalDependencies:
+ ws: 8.20.1
+ transitivePeerDependencies:
+ - '@fastify/websocket'
+ - '@types/node'
+ - '@types/react'
+ - '@types/react-dom'
+ - crossws
+ - immer
+ - react
+ - react-dom
+ - supports-color
+ - use-sync-external-store
+
grafserv@1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1):
dependencies:
'@graphile/lru': 5.0.0
@@ -17661,6 +15730,25 @@ snapshots:
- supports-color
- use-sync-external-store
+ graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1):
+ dependencies:
+ '@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)
+ '@types/node': 22.19.19
+ debug: 4.4.3(supports-color@5.5.0)
+ grafast: 1.0.2(graphql@16.13.0)
+ graphile-build: 5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)
+ graphile-config: 1.0.0
+ graphql: 16.13.0
+ jsonwebtoken: 9.0.3
+ pg-introspection: 1.0.1
+ pg-sql2: 5.0.1
+ tamedevil: 0.1.1
+ tslib: 2.8.1
+ optionalDependencies:
+ pg: 8.21.0
+ transitivePeerDependencies:
+ - supports-color
+
graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)(tamedevil@0.1.1):
dependencies:
'@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)
@@ -17699,6 +15787,25 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0):
+ dependencies:
+ '@types/node': 22.19.19
+ '@types/pluralize': 0.0.33
+ '@types/semver': 7.7.1
+ chalk: 4.1.2
+ debug: 4.4.3(supports-color@5.5.0)
+ grafast: 1.0.2(graphql@16.13.0)
+ graphile-config: 1.0.0
+ graphql: 16.13.0
+ lodash: 4.18.1
+ pluralize: 7.0.0
+ semver: 7.8.1
+ tamedevil: 0.1.1
+ transliteration: 2.6.1
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0):
dependencies:
'@types/node': 22.19.19
@@ -17718,6 +15825,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ graphile-config@1.0.0:
+ dependencies:
+ '@types/interpret': 1.1.4
+ '@types/node': 22.19.19
+ '@types/semver': 7.7.1
+ chalk: 4.1.2
+ debug: 4.4.3(supports-color@5.5.0)
+ interpret: 3.1.1
+ semver: 7.8.1
+ tslib: 2.8.1
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - supports-color
+
graphile-config@1.0.1:
dependencies:
chalk: 4.1.2
@@ -17745,6 +15866,22 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ graphile-utils@5.0.1(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(tamedevil@0.1.1):
+ dependencies:
+ '@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)
+ debug: 4.4.3(supports-color@5.5.0)
+ grafast: 1.0.2(graphql@16.13.0)
+ graphile-build: 5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)
+ graphile-config: 1.0.0
+ graphql: 16.13.0
+ json5: 2.2.3
+ tamedevil: 0.1.1
+ tslib: 2.8.1
+ optionalDependencies:
+ graphile-build-pg: 5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
graphile-utils@5.0.1(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)(tamedevil@0.1.1))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(tamedevil@0.1.1):
dependencies:
'@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)
@@ -19947,7 +18084,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
@@ -20148,6 +18285,33 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ postgraphile@5.0.0(f3ea149703b7f495547261dad1e3b475):
+ dependencies:
+ '@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
+ '@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)
+ '@graphile/lru': 5.0.0
+ '@types/node': 22.19.19
+ '@types/pg': 8.20.0
+ debug: 4.4.3(supports-color@5.5.0)
+ grafast: 1.0.2(graphql@16.13.0)
+ grafserv: 1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1)
+ graphile-build: 5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)
+ graphile-build-pg: 5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1)
+ graphile-config: 1.0.0
+ graphile-utils: 5.0.1(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(tamedevil@0.1.1)
+ graphql: 16.13.0
+ iterall: 1.3.0
+ jsonwebtoken: 9.0.3
+ pg: 8.21.0
+ pg-sql2: 5.0.1
+ tamedevil: 0.1.1
+ tslib: 2.8.1
+ ws: 8.20.1
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
postgraphile@5.0.3(02c18e7c6179c4ae54031f8cdca16ab5):
dependencies:
'@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
@@ -20778,6 +18942,27 @@ snapshots:
- immer
- use-sync-external-store
+ ruru@2.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(debug@4.4.3)(graphile-config@1.0.0)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)):
+ dependencies:
+ '@emotion/is-prop-valid': 1.4.0
+ graphile-config: 1.0.0
+ graphql: 16.13.0
+ http-proxy: 1.18.1(debug@4.4.3)
+ ruru-types: 2.0.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))
+ tslib: 2.8.1
+ yargs: 17.7.2
+ optionalDependencies:
+ react: 19.2.5
+ react-dom: 19.2.5(react@19.2.5)
+ transitivePeerDependencies:
+ - '@types/node'
+ - '@types/react'
+ - '@types/react-dom'
+ - debug
+ - graphql-ws
+ - immer
+ - use-sync-external-store
+
ruru@2.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(debug@4.4.3)(graphile-config@1.0.1)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)):
dependencies:
'@emotion/is-prop-valid': 1.4.0
From 4ec5d59d832adbc87176ae1906a6fa8a1e628aed Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Wed, 17 Jun 2026 07:33:29 +0800
Subject: [PATCH 09/17] Restore multi-tenancy cache perf changes and smoke
tests
---
.../graphile-multi-tenancy-cache/package.json | 6 +-
.../src/__tests__/buildkey.test.ts | 174 +++++------
.../graphile-multi-tenancy-cache/src/index.ts | 3 +
.../src/multi-tenancy-cache.ts | 214 +++++++++-----
.../perf/REAL_MULTITENANT_E2E_CURRENT.md | 209 ++++++++++++++
.../perf/build-business-op-profiles.mjs | 4 +-
graphql/server/perf/e2e-benchmark.ts | 10 +-
graphql/server/perf/phase1-preflight.mjs | 43 +++
.../server/perf/phase1-tech-validate-dbpm.mjs | 49 ++--
graphql/server/perf/phase2-load.mjs | 56 +++-
.../server/perf/public-test-access-lib.mjs | 31 +-
graphql/server/perf/run-comparison.sh | 53 +++-
graphql/server/perf/run-e2e-benchmark.sh | 46 ++-
graphql/server/perf/run-stress-suite.sh | 46 ++-
.../src/diagnostics/debug-memory-snapshot.ts | 3 +
graphql/server/src/middleware/auth.ts | 26 +-
graphql/server/src/middleware/graphile.ts | 118 ++++++--
graphql/server/src/server.ts | 15 +-
pnpm-lock.yaml | 269 +-----------------
19 files changed, 859 insertions(+), 516 deletions(-)
create mode 100644 graphql/server/perf/REAL_MULTITENANT_E2E_CURRENT.md
diff --git a/graphile/graphile-multi-tenancy-cache/package.json b/graphile/graphile-multi-tenancy-cache/package.json
index a0e33b1a73..6ebdef79d5 100644
--- a/graphile/graphile-multi-tenancy-cache/package.json
+++ b/graphile/graphile-multi-tenancy-cache/package.json
@@ -39,10 +39,8 @@
"dependencies": {
"@pgpmjs/logger": "workspace:^",
"express": "^5.2.1",
- "grafserv": "1.0.0",
- "graphile-config": "1.0.0",
- "pg": "^8.11.3",
- "postgraphile": "5.0.0"
+ "lru-cache": "^11.2.7",
+ "pg": "^8.11.3"
},
"devDependencies": {
"@types/express": "^5.0.6",
diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
index fc9c1856e1..c8424e086c 100644
--- a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
@@ -129,40 +129,28 @@ describe('computeBuildKey', () => {
const k2 = computeBuildKey(pool, ['services_public'], 'administrator', 'administrator');
expect(k1).toBe(k2);
});
-});
-
-// --- Orchestrator tests (require mocking PostGraphile) ---
-
-// Mock the heavy dependencies before importing the orchestrator
-jest.mock('postgraphile', () => ({
- postgraphile: jest.fn(() => ({
- createServ: jest.fn(() => ({
- addTo: jest.fn(async () => {}),
- ready: jest.fn(async () => {}),
- })),
- release: jest.fn(async () => {}),
- })),
-}));
-jest.mock('grafserv/express/v4', () => ({
- grafserv: 'mock-grafserv',
-}));
+ 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);
+ });
-jest.mock('express', () => {
- const mockExpress = jest.fn(() => {
- const app = jest.fn();
- return app;
+ 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);
});
- return mockExpress;
});
-jest.mock('node:http', () => ({
- createServer: jest.fn(() => ({
- listening: false,
- close: jest.fn((cb: () => void) => cb()),
- })),
-}));
-
jest.mock('@pgpmjs/logger', () => ({
Logger: jest.fn().mockImplementation(() => ({
info: jest.fn(),
@@ -181,17 +169,21 @@ import {
getMultiTenancyCacheStats,
shutdownMultiTenancyCache,
getBuildKeyForSvcKey,
+ type TenantHandlerFactoryContext,
+ type TenantHandlerResources,
} from '../multi-tenancy-cache';
-const mockPresetBuilder = jest.fn((_pool: import('pg').Pool, _schemas: string[], _anon: string, _role: string): import('graphile-config').GraphileConfig.Preset => ({
- extends: [] as import('graphile-config').GraphileConfig.Preset[],
- pgServices: [] as never[],
-}));
+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({ basePresetBuilder: mockPresetBuilder });
- mockPresetBuilder.mockClear();
+ configureMultiTenancyCache({ handlerFactory: mockHandlerFactory });
+ mockHandlerFactory.mockClear();
});
afterAll(async () => {
@@ -222,8 +214,8 @@ describe('getOrCreateTenantInstance — buildKey deduplication', () => {
expect(t1).toBe(t2);
expect(t1.buildKey).toBe(t2.buildKey);
- // Preset builder called only once (deduplication)
- expect(mockPresetBuilder).toHaveBeenCalledTimes(1);
+ // 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);
@@ -251,7 +243,7 @@ describe('getOrCreateTenantInstance — buildKey deduplication', () => {
expect(t1).not.toBe(t2);
expect(t1.buildKey).not.toBe(t2.buildKey);
- expect(mockPresetBuilder).toHaveBeenCalledTimes(2);
+ expect(mockHandlerFactory).toHaveBeenCalledTimes(2);
});
it('should return different handlers when roles differ', async () => {
@@ -276,6 +268,32 @@ describe('getOrCreateTenantInstance — buildKey deduplication', () => {
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', () => {
@@ -461,6 +479,24 @@ describe('shutdownMultiTenancyCache', () => {
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', () => {
@@ -533,11 +569,11 @@ describe('re-creation after flush', () => {
describe('handler creation failure — orphaned index cleanup', () => {
it('should clean up svc_key index when handler creation fails', async () => {
- // Make the preset builder throw to simulate handler creation failure
- const failingBuilder = jest.fn(() => {
+ // Make the handler factory throw to simulate handler creation failure
+ const failingFactory = jest.fn(async () => {
throw new Error('simulated build failure');
});
- configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+ configureMultiTenancyCache({ handlerFactory: failingFactory });
const pool = makeMockPool();
@@ -578,10 +614,10 @@ describe('handler creation failure — orphaned index cleanup', () => {
expect(getTenantInstance('good-key')).toBeDefined();
// Now make the next creation fail (different buildKey — different schemas)
- const failingBuilder = jest.fn(() => {
+ const failingFactory = jest.fn(async () => {
throw new Error('simulated build failure');
});
- configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+ configureMultiTenancyCache({ handlerFactory: failingFactory });
await expect(
getOrCreateTenantInstance({
@@ -671,8 +707,8 @@ describe('connectionString-based pool identity', () => {
describe('coalesced creation failure — no orphaned mappings (Finding 1)', () => {
it('should clean up both svc_keys when 2 coalesced requests fail', async () => {
- const failingBuilder = jest.fn(() => { throw new Error('coalesced fail'); });
- configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+ 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' };
@@ -697,8 +733,8 @@ describe('coalesced creation failure — no orphaned mappings (Finding 1)', () =
});
it('should clean up all svc_keys when 3+ coalesced requests fail', async () => {
- const failingBuilder = jest.fn(() => { throw new Error('coalesced fail 3+'); });
- configureMultiTenancyCache({ basePresetBuilder: failingBuilder as any });
+ 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' };
@@ -902,52 +938,28 @@ describe('svc_key rebinding — old handler cleanup (Finding 2)', () => {
describe('getOrCreateTenantInstance — svc_key race condition (epoch guard)', () => {
/**
- * Access the module-level postgraphile mock so we can override it
- * per-test with gate-controlled behaviour.
- */
- const pgMock = () =>
- (jest.requireMock('postgraphile') as { postgraphile: jest.Mock }).postgraphile;
-
- /**
- * Install a gated postgraphile mock. Each call to `postgraphile()`
- * creates a new gate; `serv.ready()` blocks until the gate is resolved.
+ * 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 installGatedMock(): Array<{ resolve: () => void }> {
+ function installGatedFactory(): Array<{ resolve: () => void }> {
const gates: Array<{ resolve: () => void }> = [];
- pgMock().mockImplementation(() => {
+ const gatedFactory = jest.fn(async (ctx: TenantHandlerFactoryContext) => {
let resolve!: () => void;
const promise = new Promise((r) => {
resolve = r;
});
gates.push({ resolve });
- return {
- createServ: jest.fn(() => ({
- addTo: jest.fn(async () => {}),
- ready: jest.fn(async () => {
- await promise;
- }),
- })),
- release: jest.fn(async () => {}),
- };
+ await promise;
+ return makeMockResources(ctx);
});
+ configureMultiTenancyCache({ handlerFactory: gatedFactory });
return gates;
}
- afterEach(() => {
- // Restore the default (instant-resolving) mock so other tests are unaffected
- pgMock().mockImplementation(() => ({
- createServ: jest.fn(() => ({
- addTo: jest.fn(async () => {}),
- ready: jest.fn(async () => {}),
- })),
- release: jest.fn(async () => {}),
- }));
- });
-
it('newer request finishes first — final mapping stays on newer buildKey', async () => {
- const gates = installGatedMock();
+ const gates = installGatedFactory();
const pool = makeMockPool();
// Start OLDER request (buildKey A — schemas=['schema_old'])
@@ -988,7 +1000,7 @@ describe('getOrCreateTenantInstance — svc_key race condition (epoch guard)', (
});
it('older request finishes first — final mapping ends on newer buildKey', async () => {
- const gates = installGatedMock();
+ const gates = installGatedFactory();
const pool = makeMockPool();
const pOld = getOrCreateTenantInstance({
@@ -1027,7 +1039,7 @@ describe('getOrCreateTenantInstance — svc_key race condition (epoch guard)', (
});
it('no orphaned handler/index state remains after race', async () => {
- const gates = installGatedMock();
+ const gates = installGatedFactory();
const pool = makeMockPool();
const pOld = getOrCreateTenantInstance({
diff --git a/graphile/graphile-multi-tenancy-cache/src/index.ts b/graphile/graphile-multi-tenancy-cache/src/index.ts
index 03a60b3626..d5fedbb777 100644
--- a/graphile/graphile-multi-tenancy-cache/src/index.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/index.ts
@@ -13,6 +13,9 @@ export {
export type {
TenantConfig,
+ TenantHandlerFactory,
+ TenantHandlerFactoryContext,
+ TenantHandlerResources,
TenantInstance,
MultiTenancyCacheStats,
MultiTenancyCacheConfig,
diff --git a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
index f117831755..cd73761552 100644
--- a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
@@ -16,13 +16,11 @@
* databaseIdToBuildKeys: databaseId → Set
*/
-import { createServer } from 'node:http';
import { Logger } from '@pgpmjs/logger';
-import express from 'express';
-import { postgraphile } from 'postgraphile';
-import { grafserv } from 'grafserv/express/v4';
+import type { Express } from 'express';
+import type { Server as HttpServer } from 'node:http';
import type { Pool } from 'pg';
-import type { GraphileConfig } from 'graphile-config';
+import { LRUCache } from 'lru-cache';
const log = new Logger('multi-tenancy-cache');
@@ -35,23 +33,32 @@ export interface TenantConfig {
anonRole: string;
roleName: string;
databaseId?: string;
+ presetOptions?: unknown;
}
-export interface TenantInstance {
+export interface TenantHandlerResources {
+ handler: Express;
+ httpServer?: HttpServer;
+ pgl?: { release(): void | PromiseLike };
+ realtimeManager?: { stop(): Promise } | null;
+ release?: () => Promise;
+}
+
+export interface TenantInstance extends TenantHandlerResources {
buildKey: string;
- handler: import('express').Express;
schemas: string[];
- pgl: import('postgraphile').PostGraphileInstance;
- httpServer: import('http').Server;
createdAt: number;
lastUsedAt: number;
}
export interface MultiTenancyCacheStats {
handlerCacheSize: number;
+ handlerCacheMax: number;
+ handlerCacheTtlMs: number;
svcKeyMappings: number;
databaseIdMappings: number;
inflightCreations: number;
+ buildKeys: string[];
}
interface BuildKeyParts {
@@ -59,12 +66,66 @@ interface BuildKeyParts {
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;
+
// --- Internal state ---
+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;
+
+const getHandlerCacheConfig = () => {
+ 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,
+ ),
+ };
+};
+
+const initialCacheConfig = getHandlerCacheConfig();
+
+/** Tenant resources that have already been disposed. Prevents double release via manual + LRU paths. */
+const disposedTenants = new WeakSet();
+
/** buildKey → TenantInstance (the real handler cache) */
-const handlerCache = new Map();
+const handlerCache = new LRUCache({
+ max: initialCacheConfig.max,
+ ttl: initialCacheConfig.ttlMs,
+ updateAgeOnGet: true,
+ dispose: (handler, buildKey) => {
+ removeIndexesForBuildKey(buildKey);
+ disposeTenant(handler).catch((err) => {
+ log.error(`Failed to dispose handler buildKey=${buildKey}:`, err);
+ });
+ },
+});
/** svc_key → buildKey (routing index) */
const svcKeyToBuildKey = new Map();
@@ -87,31 +148,21 @@ const creatingHandlers = new Map>();
*/
const svcKeyEpoch = new Map();
-/** The preset builder, set once by configureMultiTenancyCache(). */
-let presetBuilder: ((
- pool: Pool,
- schemas: string[],
- anonRole: string,
- roleName: string,
-) => GraphileConfig.Preset) | null = null;
+/** The handler factory, set once by configureMultiTenancyCache(). */
+let handlerFactory: TenantHandlerFactory | null = null;
// --- Configuration ---
export interface MultiTenancyCacheConfig {
- basePresetBuilder: (
- pool: Pool,
- schemas: string[],
- anonRole: string,
- roleName: string,
- ) => GraphileConfig.Preset;
+ handlerFactory: TenantHandlerFactory;
}
/**
- * One-time package bootstrap. Stores the preset builder.
+ * One-time package bootstrap. Stores the handler factory.
* Must be called before any getOrCreateTenantInstance() calls.
*/
export function configureMultiTenancyCache(config: MultiTenancyCacheConfig): void {
- presetBuilder = config.basePresetBuilder;
+ handlerFactory = config.handlerFactory;
log.info('Multi-tenancy cache configured (buildKey-based handler caching)');
}
@@ -156,6 +207,24 @@ function getPoolIdentity(pool: Pool): string {
return 'unknown-pool';
}
+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.
@@ -180,6 +249,7 @@ export function computeBuildKey(
schemas: string[],
anonRole: string,
roleName: string,
+ presetOptions?: unknown,
): string {
const input: BuildKeyParts = {
conn: getPoolIdentity(pool),
@@ -187,6 +257,10 @@ export function computeBuildKey(
anonRole,
roleName,
};
+ const normalizedPresetOptions = normalizeBuildInput(presetOptions);
+ if (normalizedPresetOptions !== undefined) {
+ input.presetOptions = normalizedPresetOptions;
+ }
return JSON.stringify(input);
}
@@ -234,37 +308,31 @@ function getSvcKeysForBuildKey(buildKey: string): string[] {
return result;
}
-/**
- * Remove a buildKey from all indexes and dispose the handler (if present).
- *
- * Also cleans orphaned indexes — if handler creation failed, the handler
- * won't be in handlerCache but the svc_key and databaseId indexes may
- * still reference the buildKey. This function cleans those too.
- */
-function evictBuildKey(buildKey: string): void {
- const handler = handlerCache.get(buildKey);
-
- // Always clean indexes, even if handler isn't cached (handles orphaned indexes)
- handlerCache.delete(buildKey);
-
- // Remove all svc_key → buildKey mappings pointing to this buildKey
+function removeIndexesForBuildKey(buildKey: string): void {
for (const svcKey of getSvcKeysForBuildKey(buildKey)) {
svcKeyToBuildKey.delete(svcKey);
}
- // Remove from databaseId index
for (const [dbId, keys] of databaseIdToBuildKeys) {
keys.delete(buildKey);
if (keys.size === 0) databaseIdToBuildKeys.delete(dbId);
}
+}
- if (handler) {
- disposeTenant(handler).catch((err) => {
- log.error(`Failed to dispose handler buildKey=${buildKey}:`, err);
- });
+/**
+ * Remove a buildKey from all indexes and dispose the handler (if present).
+ *
+ * Also cleans orphaned indexes — if handler creation failed, the handler
+ * won't be in handlerCache but the svc_key and databaseId indexes may
+ * still reference the buildKey. This function cleans those too.
+ */
+function evictBuildKey(buildKey: string): void {
+ const deleted = handlerCache.delete(buildKey);
+ if (!deleted) {
+ removeIndexesForBuildKey(buildKey);
}
- log.debug(`Evicted buildKey=${buildKey} (handler=${handler ? 'disposed' : 'none/orphaned'})`);
+ log.debug(`Evicted buildKey=${buildKey} (handler=${deleted ? 'disposed' : 'none/orphaned'})`);
}
// --- Core API ---
@@ -297,7 +365,7 @@ export function getBuildKeyForSvcKey(svcKey: string): string | undefined {
* 1. Compute buildKey from config's build inputs
* 2. Check handlerCache (fast path) → register mapping, return
* 3. Check creatingHandlers (single-flight coalesce) → await, register on success
- * 4. Create a new independent PostGraphile instance keyed by buildKey
+ * 4. Create a new independent handler keyed by buildKey
* 5. On success: register mapping, store in handlerCache, return
*
* Registration is deferred until AFTER handler creation succeeds. This
@@ -307,13 +375,13 @@ export function getBuildKeyForSvcKey(svcKey: string): string | undefined {
export async function getOrCreateTenantInstance(
config: TenantConfig,
): Promise {
- const { svcKey, pool, schemas, anonRole, roleName, databaseId } = config;
+ const { svcKey, pool, schemas, anonRole, roleName, databaseId, presetOptions } = config;
- if (!presetBuilder) {
+ if (!handlerFactory) {
throw new Error('Multi-tenancy cache not configured. Call configureMultiTenancyCache() first.');
}
- const buildKey = computeBuildKey(pool, schemas, anonRole, roleName);
+ const buildKey = computeBuildKey(pool, schemas, anonRole, roleName, presetOptions);
// Capture a monotonically increasing epoch for this svc_key.
// Only the request holding the latest epoch is allowed to register.
@@ -342,7 +410,16 @@ export async function getOrCreateTenantInstance(
}
// Step 3: Creator path — build a new handler
- const promise = doCreateHandler(buildKey, pool, schemas, anonRole, roleName);
+ const promise = doCreateHandler({
+ buildKey,
+ svcKey,
+ pool,
+ schemas,
+ anonRole,
+ roleName,
+ databaseId,
+ presetOptions,
+ });
creatingHandlers.set(buildKey, promise);
try {
@@ -366,31 +443,19 @@ export async function getOrCreateTenantInstance(
}
async function doCreateHandler(
- buildKey: string,
- pool: Pool,
- schemas: string[],
- anonRole: string,
- roleName: string,
+ context: TenantHandlerFactoryContext,
): Promise {
+ const { buildKey, schemas } = context;
const schemaLabel = schemas.join(',') || 'unknown';
log.info(`Building handler buildKey=${buildKey} schemas=${schemaLabel}`);
- const preset = presetBuilder!(pool, schemas, anonRole, roleName);
- const pgl = postgraphile(preset);
- const serv = pgl.createServ(grafserv);
-
- const handler = express();
- const httpServer = createServer(handler);
- await serv.addTo(handler, httpServer);
- await serv.ready();
+ const resources = await handlerFactory!(context);
const tenant: TenantInstance = {
+ ...resources,
buildKey,
- handler,
schemas,
- pgl,
- httpServer,
createdAt: Date.now(),
lastUsedAt: Date.now(),
};
@@ -436,12 +501,24 @@ export function flushByDatabaseId(databaseId: string): void {
}
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();
}
@@ -456,9 +533,12 @@ async function disposeTenant(tenant: TenantInstance): Promise {
export function getMultiTenancyCacheStats(): MultiTenancyCacheStats {
return {
handlerCacheSize: handlerCache.size,
+ handlerCacheMax: initialCacheConfig.max,
+ handlerCacheTtlMs: initialCacheConfig.ttlMs,
svcKeyMappings: svcKeyToBuildKey.size,
databaseIdMappings: databaseIdToBuildKeys.size,
inflightCreations: creatingHandlers.size,
+ buildKeys: [...handlerCache.keys()],
};
}
@@ -481,7 +561,7 @@ export async function shutdownMultiTenancyCache(): Promise {
databaseIdToBuildKeys.clear();
creatingHandlers.clear();
svcKeyEpoch.clear();
- presetBuilder = null;
+ handlerFactory = null;
log.info('Multi-tenancy cache shutdown complete');
}
diff --git a/graphql/server/perf/REAL_MULTITENANT_E2E_CURRENT.md b/graphql/server/perf/REAL_MULTITENANT_E2E_CURRENT.md
new file mode 100644
index 0000000000..ccbdf31345
--- /dev/null
+++ b/graphql/server/perf/REAL_MULTITENANT_E2E_CURRENT.md
@@ -0,0 +1,209 @@
+# Real Multitenant E2E Perf Runbook
+
+This is the current verified runbook for the real DBPM-backed multitenant perf
+lane in this branch. It supersedes the older local notes that assumed a single
+private route and a `postgres_perf` database.
+
+## Database
+
+Use an already deployed Constructive database. The perf scripts default to:
+
+```bash
+PGHOST=localhost
+PGPORT=5432
+PGUSER=postgres
+PGPASSWORD=password
+PGDATABASE=constructive
+```
+
+`postgres_perf` is only an optional isolated database name from older local
+notes. This branch does not include a perf-local script that creates and deploys
+that database from zero. If you want isolation, create and deploy it externally,
+then export `PGDATABASE=postgres_perf` before running these scripts.
+
+## Server Modes
+
+The current DBPM flow needs the server in public routing mode:
+
+```bash
+cd /Users/zeta/Projects/interweb/src/agents/constructive/graphql/server
+
+PGHOST=localhost \
+PGPORT=5432 \
+PGUSER=postgres \
+PGPASSWORD=password \
+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 \
+npx ts-node src/run.ts
+```
+
+Why public mode: current local DBPM provisioning exposes auth/provision/business
+through public hosts:
+
+- `auth.localhost` for `signUp` and `signIn`
+- `modules.localhost` for DBPM provisioning mutations
+- `api-dbpm-*.localhost` for provisioned business-table GraphQL
+
+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 created here do not currently create private business APIs.
+
+## Phase 1: Provision Tenants
+
+From the repo root:
+
+```bash
+cd /Users/zeta/Projects/interweb/src/agents/constructive
+
+RUN_DIR=/tmp/constructive-perf/dbpm-$(date +%Y%m%d-%H%M%S)
+
+NO_PROXY=localhost,127.0.0.1,::1 \
+no_proxy=localhost,127.0.0.1,::1 \
+node graphql/server/perf/phase1-preflight.mjs \
+ --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 for a small local run:
+
+```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 the phase 2 smoke command below.
+
+## Phase 2: Business Load
+
+Run a short correctness load first:
+
+```bash
+node graphql/server/perf/phase2-load.mjs \
+ --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:
+
+```json
+{
+ "profileCount": 2,
+ "failed": 0
+}
+```
+
+`--public-role anonymous` is required for the current public business route
+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 script only prepares schemas whose names start
+with `perf-` unless explicitly overridden.
+
+For a longer load, increase `--workers`, `--duration-seconds`, and remove
+`--disable-prewarm` after the short correctness run passes.
+
+## Lightweight HTTP Lane
+
+The lightweight benchmark is separate from DBPM and uses synthetic private
+header routing. Start the server in private mode:
+
+```bash
+cd /Users/zeta/Projects/interweb/src/agents/constructive/graphql/server
+
+PGHOST=localhost \
+PGPORT=5432 \
+PGUSER=postgres \
+PGPASSWORD=password \
+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 \
+npx ts-node src/run.ts
+```
+
+Then run:
+
+```bash
+cd /Users/zeta/Projects/interweb/src/agents/constructive
+
+MODE=new \
+K=2 \
+DURATION=2 \
+WORKERS=1 \
+SERVER_PORT=3000 \
+NO_PROXY=localhost,127.0.0.1,::1 \
+no_proxy=localhost,127.0.0.1,::1 \
+npx ts-node graphql/server/perf/e2e-benchmark.ts
+```
+
+Important: `MODE=new` labels the client-side benchmark output. It does not
+switch an already running server. The server must be started with
+`USE_MULTI_TENANCY_CACHE=true` to exercise the new cache path.
+
+For this branch's exact-match buildKey cache, multiple synthetic tenants with
+the same connection/schemas/roles/settings should map to one handler build.
+Use `/debug/memory` to confirm `multiTenancyCache.handlerCacheSize` and
+`graphileBuilds.started`.
+
+## Cleanup
+
+`reset-business-test-data.mjs` truncates business workload tables for a run. It
+does not drop DBPM-created tenant databases, API/domain rows, 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`.
+- `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.
diff --git a/graphql/server/perf/build-business-op-profiles.mjs b/graphql/server/perf/build-business-op-profiles.mjs
index 89b5e44d6e..c8c40c98be 100644
--- a/graphql/server/perf/build-business-op-profiles.mjs
+++ b/graphql/server/perf/build-business-op-profiles.mjs
@@ -34,8 +34,8 @@ const compatRoutingMode = getArgValue(
)
.trim()
.toLowerCase();
-const publicApiName = getArgValue(args, '--public-api-name', 'app').trim();
-const publicSubdomainPrefix = getArgValue(args, '--public-subdomain-prefix', 'app-public-').trim();
+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')),
diff --git a/graphql/server/perf/e2e-benchmark.ts b/graphql/server/perf/e2e-benchmark.ts
index ad15757135..6737a748e2 100644
--- a/graphql/server/perf/e2e-benchmark.ts
+++ b/graphql/server/perf/e2e-benchmark.ts
@@ -124,7 +124,15 @@ async function getHeapUsedMB(): Promise {
.on('error', reject);
});
const parsed = JSON.parse(res);
- if (parsed.heapUsed) return parsed.heapUsed / 1024 / 1024;
+ 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
}
diff --git a/graphql/server/perf/phase1-preflight.mjs b/graphql/server/perf/phase1-preflight.mjs
index aeca1ecc04..2f590b8cb1 100644
--- a/graphql/server/perf/phase1-preflight.mjs
+++ b/graphql/server/perf/phase1-preflight.mjs
@@ -82,6 +82,23 @@ 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')),
);
@@ -281,6 +298,24 @@ const runDbpmTechValidationScript = async () => {
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'] },
);
@@ -324,6 +359,14 @@ const runBusinessProfileBuilder = async () => {
tokenOutputPath,
'--output',
businessProfilesOutputPath,
+ '--routing-mode',
+ businessRoutingMode,
+ '--compat-routing-mode',
+ businessCompatRoutingMode,
+ '--public-api-name',
+ businessPublicApiName,
+ '--public-subdomain-prefix',
+ businessPublicSubdomainPrefix,
],
{ stdio: ['ignore', 'pipe', 'pipe'] },
);
diff --git a/graphql/server/perf/phase1-tech-validate-dbpm.mjs b/graphql/server/perf/phase1-tech-validate-dbpm.mjs
index 9b2f4f20a7..d9298248bb 100644
--- a/graphql/server/perf/phase1-tech-validate-dbpm.mjs
+++ b/graphql/server/perf/phase1-tech-validate-dbpm.mjs
@@ -47,12 +47,16 @@ const privateDatabaseId = getArgValue(
'--private-database-id',
'028752cb-510b-1438-2f39-64534bd1cbd7',
);
+const authHost = getArgValue(args, '--auth-host', '');
+const provisionHost = getArgValue(args, '--provision-host', '');
-const routeHeaders = {
+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(',')
@@ -67,6 +71,9 @@ if (modules.length === 0) {
// 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).
@@ -84,8 +91,8 @@ const VARIANT_DEFS = [
{
suffix: 'tags',
fields: [
- { name: 'label', type: 'text' },
- { name: 'priority', type: 'integer' },
+ provisionField('label', 'text'),
+ provisionField('priority', 'integer'),
],
},
],
@@ -96,9 +103,9 @@ const VARIANT_DEFS = [
{
suffix: 'metrics',
fields: [
- { name: 'value', type: 'numeric' },
- { name: 'recorded_at', type: 'timestamptz' },
- { name: 'active', type: 'boolean' },
+ provisionField('value', 'numeric'),
+ provisionField('recorded_at', 'timestamptz'),
+ provisionField('active', 'boolean'),
],
},
],
@@ -170,14 +177,13 @@ mutation CreateSecureTableProvision($input: CreateSecureTableProvisionInput!) {
schemaId
tableId
tableName
- nodeType
outFields
}
}
}
`;
-const gql = async ({ query, variables, headers = {}, timeoutMs = 30000 }) => {
+const gql = async ({ query, variables, headers = {}, routeHeaders = provisionRouteHeaders, timeoutMs = 30000 }) => {
return await postJson({
url: `${baseUrl}/graphql`,
headers: {
@@ -189,11 +195,18 @@ const gql = async ({ query, variables, headers = {}, timeoutMs = 30000 }) => {
});
};
-const firstError = (response) => response.json?.errors?.[0]?.message || response.error || 'unknown';
+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,
@@ -215,6 +228,7 @@ const signUpOrSignInUser = async ({ email, password }) => {
const signInRes = await gql({
query: signInMutation,
+ routeHeaders: authRouteHeaders,
variables: {
input: {
email,
@@ -363,8 +377,8 @@ const createBusinessTable = async ({ token, databaseId, schemaId, tableName }) =
databaseId,
schemaId,
tableName,
- nodeType: 'DataId',
- fields: [{ name: 'note', type: 'text' }],
+ nodes: ['DataId'],
+ fields: [provisionField('note', 'text')],
},
},
},
@@ -397,7 +411,7 @@ const createVariantTable = async ({ token, databaseId, schemaId, tableName, fiel
databaseId,
schemaId,
tableName,
- nodeType: 'DataId',
+ nodes: ['DataId'],
fields,
},
},
@@ -615,7 +629,6 @@ const main = async () => {
schemaId: secureTable.schemaId,
tableId: secureTable.tableId,
tableName: secureTable.tableName,
- nodeType: secureTable.nodeType,
outFields: secureTable.outFields,
},
},
@@ -693,7 +706,11 @@ const main = async () => {
const report = {
createdAt: new Date().toISOString(),
baseUrl,
- routeHeaders,
+ 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,
@@ -729,8 +746,8 @@ const main = async () => {
tenantKey: account.tenantKey,
email: account.email,
password: userPassword,
- host: routeHost,
- apiName: privateApiName,
+ host: authRouteHeaders.Host,
+ ...(authRouteHeaders['X-Api-Name'] ? { apiName: authRouteHeaders['X-Api-Name'] } : {}),
databaseId: account.created.provisionFinal.databaseId,
provisionedDatabaseId: account.created.provisionFinal.databaseId,
}));
diff --git a/graphql/server/perf/phase2-load.mjs b/graphql/server/perf/phase2-load.mjs
index 645b2ac30b..b4741793a8 100644
--- a/graphql/server/perf/phase2-load.mjs
+++ b/graphql/server/perf/phase2-load.mjs
@@ -159,11 +159,55 @@ const nonNegativeDelta = (startValue, endValue) => {
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);
@@ -430,7 +474,8 @@ const analyzeCacheRedundancyRisk = ({ baselineSnapshot, afterSnapshot, idleSnaps
};
};
-const extractCacheActivity = (debugPayload) => debugPayload?.json?.graphileCacheActivity ?? null;
+const extractCacheActivity = (debugPayload) =>
+ debugPayload?.json?.graphileCacheActivity ?? debugPayload?.json?.multiTenancyCache ?? null;
const clamp01 = (value) => {
if (!Number.isFinite(value)) return 0;
@@ -589,7 +634,7 @@ const buildBusinessRequest = ({ profile, operation, rowId }) => {
return {
operation: 'getById',
payload: {
- query: `query($id:UUID!){${table.queryField}(condition:{id:$id},first:1){nodes{id note}}}`,
+ query: `query($id:UUID!){${table.queryField}(where:{id:{equalTo:$id}},first:1){nodes{id note}}}`,
variables: {
id: rowId,
},
@@ -735,15 +780,18 @@ const runRouteProbe = async ({ 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: { query: '{ __typename }' },
+ payload,
timeoutMs: 15000,
});
const hasGraphQLErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0;
- const ok = result.ok && !hasGraphQLErrors && result.json?.data?.__typename === 'Query';
+ const ok = result.ok && !hasGraphQLErrors && result.json?.data != null;
const probe = {
attempted: true,
diff --git a/graphql/server/perf/public-test-access-lib.mjs b/graphql/server/perf/public-test-access-lib.mjs
index 354ad9f526..e59c1447ce 100644
--- a/graphql/server/perf/public-test-access-lib.mjs
+++ b/graphql/server/perf/public-test-access-lib.mjs
@@ -12,6 +12,24 @@ const POLICY_NAMES = {
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;
@@ -103,6 +121,7 @@ export const ensurePublicAccessForTargets = async ({
const createdPolicies = [];
if (rlsEnabled) {
+ const policyNames = policyNamesForRole(publicRole);
const policyResult = await pool.query(
`
select policyname
@@ -121,16 +140,16 @@ export const ensurePublicAccessForTargets = async ({
};
await maybeCreatePolicy(
- POLICY_NAMES.select,
- `create policy ${quoteIdent(POLICY_NAMES.select)} on ${qualified} for select to ${publicRoleIdent} using (true);`,
+ policyNames.select,
+ `create policy ${quoteIdent(policyNames.select)} on ${qualified} for select to ${publicRoleIdent} using (true);`,
);
await maybeCreatePolicy(
- POLICY_NAMES.insert,
- `create policy ${quoteIdent(POLICY_NAMES.insert)} on ${qualified} for insert to ${publicRoleIdent} with check (true);`,
+ policyNames.insert,
+ `create policy ${quoteIdent(policyNames.insert)} on ${qualified} for insert to ${publicRoleIdent} with check (true);`,
);
await maybeCreatePolicy(
- POLICY_NAMES.update,
- `create policy ${quoteIdent(POLICY_NAMES.update)} on ${qualified} for update to ${publicRoleIdent} using (true) with check (true);`,
+ policyNames.update,
+ `create policy ${quoteIdent(policyNames.update)} on ${qualified} for update to ${publicRoleIdent} using (true) with check (true);`,
);
}
diff --git a/graphql/server/perf/run-comparison.sh b/graphql/server/perf/run-comparison.sh
index 35cd8bd5fd..845ab50523 100755
--- a/graphql/server/perf/run-comparison.sh
+++ b/graphql/server/perf/run-comparison.sh
@@ -29,6 +29,7 @@ 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
@@ -56,11 +57,15 @@ export PGHOST="${PGHOST:-localhost}"
export PGPORT="${PGPORT:-5432}"
export PGUSER="${PGUSER:-postgres}"
export PGPASSWORD="${PGPASSWORD:-password}"
-export PGDATABASE="${PGDATABASE:-postgres}"
+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=false
+export K DURATION WORKERS SERVER_PORT
echo "=============================================================="
echo "E2E Multi-Tenancy Comparison (Perf Framework)"
@@ -72,15 +77,39 @@ echo "=============================================================="
mkdir -p "$RUN_DIR"
kill_server() {
- fuser -k ${SERVER_PORT}/tcp 2>/dev/null || true
- sleep 2
+ 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 -sf "${BASE_URL}/debug/memory" >/dev/null 2>&1; do
+ 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
@@ -89,6 +118,10 @@ wait_for_server() {
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)"
}
@@ -104,7 +137,7 @@ start_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 (shared templates)"
+ 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"
@@ -122,10 +155,12 @@ start_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^^} mode benchmark (k=$K, ${DURATION}s, ${WORKERS} workers)"
+ echo "Running ${mode_label} mode benchmark (k=$K, ${DURATION}s, ${WORKERS} workers)"
echo "=============================================================="
cd "$SERVER_DIR"
@@ -135,14 +170,14 @@ run_e2e_benchmark() {
SERVER_PORT="$SERVER_PORT" \
npx ts-node perf/e2e-benchmark.ts 2>&1 | tee "$RUN_DIR/benchmark-${mode}-output.txt"
- echo " ${mode^^} mode complete."
+ echo " ${mode_label} mode complete."
}
# Capture server memory snapshot
capture_memory() {
local label="$1"
local outfile="$RUN_DIR/memory-${label}.json"
- curl -sf "${BASE_URL}/debug/memory" > "$outfile" 2>/dev/null || echo '{"error":"failed"}' > "$outfile"
+ curl --noproxy '*' -sf "${BASE_URL}/debug/memory" > "$outfile" 2>/dev/null || echo '{"error":"failed"}' > "$outfile"
echo " Memory snapshot: $outfile"
}
@@ -228,7 +263,7 @@ run_e2e_benchmark "old"
capture_memory "old-after"
kill_server
-# Phase B: NEW mode (multi-tenancy cache with shared templates)
+# Phase B: NEW mode (multi-tenancy cache with buildKey handler reuse)
start_server "new"
capture_memory "new-before"
run_e2e_benchmark "new"
diff --git a/graphql/server/perf/run-e2e-benchmark.sh b/graphql/server/perf/run-e2e-benchmark.sh
index 722430b7d6..dbe682f850 100755
--- a/graphql/server/perf/run-e2e-benchmark.sh
+++ b/graphql/server/perf/run-e2e-benchmark.sh
@@ -18,6 +18,7 @@ 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
@@ -36,11 +37,15 @@ export PGHOST="${PGHOST:-localhost}"
export PGPORT="${PGPORT:-5432}"
export PGUSER="${PGUSER:-postgres}"
export PGPASSWORD="${PGPASSWORD:-password}"
-export PGDATABASE="${PGDATABASE:-postgres}"
+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=false
+MODE_LABEL="$(printf '%s' "$MODE" | tr '[:lower:]' '[:upper:]')"
echo "=============================================================="
echo "E2E Multi-Tenancy Benchmark (Single Mode)"
@@ -48,15 +53,37 @@ echo " Mode=$MODE, K=$K tenants, Duration=${DURATION}s, Workers=$WORKERS"
echo "=============================================================="
kill_server() {
- fuser -k ${SERVER_PORT}/tcp 2>/dev/null || true
- sleep 2
+ 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 -sf "${BASE_URL}/debug/memory" >/dev/null 2>&1; do
+ 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
@@ -65,15 +92,20 @@ wait_for_server() {
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 (shared templates)"
+ echo " USE_MULTI_TENANCY_CACHE=true (buildKey handler reuse)"
else
unset USE_MULTI_TENANCY_CACHE 2>/dev/null || true
OLD_CACHE_MAX=$(( K * 6 ))
@@ -93,7 +125,7 @@ wait_for_server
echo ""
echo "=============================================================="
-echo "Running ${MODE^^} mode benchmark (k=$K, ${DURATION}s, ${WORKERS} workers)"
+echo "Running ${MODE_LABEL} mode benchmark (k=$K, ${DURATION}s, ${WORKERS} workers)"
echo "=============================================================="
MODE="$MODE" K="$K" DURATION="$DURATION" WORKERS="$WORKERS" \
@@ -104,7 +136,7 @@ echo ""
echo "Benchmark complete."
# Capture final memory
-curl -sf "${BASE_URL}/debug/memory" > /tmp/memory-${MODE}-final.json 2>/dev/null || true
+curl --noproxy '*' -sf "${BASE_URL}/debug/memory" > /tmp/memory-${MODE}-final.json 2>/dev/null || true
kill_server
diff --git a/graphql/server/perf/run-stress-suite.sh b/graphql/server/perf/run-stress-suite.sh
index 22acf020a4..542077577a 100644
--- a/graphql/server/perf/run-stress-suite.sh
+++ b/graphql/server/perf/run-stress-suite.sh
@@ -3,7 +3,7 @@
# Usage: bash perf/run-stress-suite.sh
#
# Requires: server NOT running (this script manages server lifecycle)
-# Requires: postgres_perf database with test data
+# Requires: a deployed Constructive database; defaults to PGDATABASE=constructive
set -euo pipefail
@@ -12,18 +12,42 @@ SERVER_DIR="$(dirname "$SCRIPT_DIR")"
cd "$SERVER_DIR"
# Common env
-export PGHOST=127.0.0.1 PGPORT=5432 PGUSER=postgres PGPASSWORD=password PGDATABASE=postgres_perf
+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() {
- fuser -k $SERVER_PORT/tcp 2>/dev/null || true
- sleep 2
+ 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:-}"
@@ -42,7 +66,11 @@ start_server() {
# Wait for server to be ready
for i in $(seq 1 30); do
- if curl -sf http://localhost:$SERVER_PORT/debug/memory > /dev/null 2>&1; then
+ 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
@@ -53,7 +81,7 @@ start_server() {
}
capture_memory() {
- curl -sf http://localhost:$SERVER_PORT/debug/memory 2>/dev/null || echo '{}'
+ curl --noproxy '*' -sf http://localhost:$SERVER_PORT/debug/memory 2>/dev/null || echo '{}'
}
run_test() {
@@ -79,7 +107,11 @@ import sys,json
try:
d=json.load(sys.stdin)
m=d.get('memory',d)
- print(f' Heap: {m.get(\"heapUsed\",0)/1024/1024:.1f} MB, RSS: {m.get(\"rss\",0)/1024/1024:.1f} MB')
+ 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\",\"?\")}')
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/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/graphile.ts b/graphql/server/src/middleware/graphile.ts
index 98d298c0ca..a1724a4523 100644
--- a/graphql/server/src/middleware/graphile.ts
+++ b/graphql/server/src/middleware/graphile.ts
@@ -9,12 +9,12 @@ 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,
- flushByDatabaseId,
} from 'graphile-multi-tenancy-cache';
import './types'; // for Request type
import { isGraphqlObservabilityEnabled } from '../diagnostics/observability';
@@ -210,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,
@@ -306,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) => {
@@ -384,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 {
@@ -454,14 +491,38 @@ export async function shutdownMultiTenancy(): Promise {
* 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() — no preset builder passed from server.
- * Routes request to tenant.handler — the package's Grafast context callback
- * handles pgSqlTextTransform injection internally.
+ * 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 preset builder
configureMultiTenancyCache({
- basePresetBuilder: buildPreset,
+ 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');
@@ -505,6 +566,7 @@ export const multiTenancyHandler = (opts: ConstructiveOptions): RequestHandler =
anonRole,
roleName,
databaseId: api.databaseId,
+ presetOptions: api.databaseSettings,
});
return tenant.handler(req, res, next);
@@ -519,3 +581,17 @@ export const multiTenancyHandler = (opts: ConstructiveOptions): RequestHandler =
}
};
};
+
+/**
+ * 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 a70ae20712..4e62d9f99f 100644
--- a/graphql/server/src/server.ts
+++ b/graphql/server/src/server.ts
@@ -33,11 +33,9 @@ import { createAuthenticateMiddleware } from './middleware/auth';
import { cors } from './middleware/cors';
import { errorHandler, notFoundHandler } from './middleware/error-handler';
import { favicon } from './middleware/favicon';
-import { flush, createFlushMiddleware, flushService } from './middleware/flush';
+import { createFlushMiddleware, flushService } from './middleware/flush';
import {
graphile,
- multiTenancyHandler,
- isMultiTenancyCacheEnabled,
shutdownMultiTenancy,
} from './middleware/graphile';
import { multipartBridge } from './middleware/multipart-bridge';
@@ -236,15 +234,8 @@ class Server {
// routes are handled without going through PostGraphile
app.use(createLlmApiRouter());
- // Select handler based on multi-tenancy cache mode
- if (isMultiTenancyCacheEnabled(effectiveOpts)) {
- log.info('[server] Multi-tenancy cache ENABLED');
- app.use(multiTenancyHandler(effectiveOpts));
- app.use(createFlushMiddleware(effectiveOpts));
- } else {
- app.use(graphile(effectiveOpts));
- app.use(flush);
- }
+ app.use(graphile(effectiveOpts));
+ app.use(createFlushMiddleware(effectiveOpts));
// Error handling - MUST be LAST
app.use(notFoundHandler); // Catches unmatched routes (404)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8b4a1296c8..9642d096ab 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -620,18 +620,12 @@ importers:
express:
specifier: ^5.2.1
version: 5.2.1
- grafserv:
- specifier: 1.0.0
- version: 1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1)
- graphile-config:
- specifier: 1.0.0
- version: 1.0.0
+ lru-cache:
+ specifier: ^11.2.7
+ version: 11.2.7
pg:
specifier: ^8.11.3
version: 8.21.0
- postgraphile:
- specifier: 5.0.0
- version: 5.0.0(f3ea149703b7f495547261dad1e3b475)
devDependencies:
'@types/express':
specifier: ^5.0.6
@@ -1700,9 +1694,6 @@ importers:
'@constructive-io/s3-utils':
specifier: workspace:^
version: link:../../uploads/s3-utils/dist
- '@constructive-io/upload-names':
- specifier: workspace:^
- version: link:../../uploads/upload-names/dist
'@constructive-io/url-domains':
specifier: workspace:^
version: link:../../packages/url-domains/dist
@@ -1721,9 +1712,6 @@ importers:
'@pgpmjs/types':
specifier: workspace:^
version: link:../../pgpm/types/dist
- '@pgsql/quotes':
- specifier: ^17.1.0
- version: 17.1.0
cors:
specifier: ^2.8.6
version: 2.8.6
@@ -1775,9 +1763,6 @@ importers:
lru-cache:
specifier: ^11.2.7
version: 11.2.7
- multer:
- specifier: ^2.1.1
- version: 2.1.1
pg:
specifier: ^8.21.0
version: 8.21.0
@@ -1815,9 +1800,6 @@ importers:
'@types/graphql-upload':
specifier: ^8.0.12
version: 8.0.12
- '@types/multer':
- specifier: ^2.1.0
- version: 2.1.0
'@types/pg':
specifier: ^8.20.0
version: 8.20.0
@@ -5931,9 +5913,6 @@ packages:
'@types/http-errors@2.0.5':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
- '@types/interpret@1.1.4':
- resolution: {integrity: sha512-r+tPKWHYqaxJOYA3Eik0mMi+SEREqOXLmsooRFmc6GHv7nWUDixFtKN+cegvsPlDcEZd9wxsdp041v2imQuvag==}
-
'@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
@@ -5976,9 +5955,6 @@ packages:
'@types/minimist@1.2.5':
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
- '@types/multer@2.1.0':
- resolution: {integrity: sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA==}
-
'@types/node@22.19.11':
resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==}
@@ -6345,9 +6321,6 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
- append-field@1.0.0:
- resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
-
appstash@0.7.0:
resolution: {integrity: sha512-UExc8kEseReJRbllAkQ/qeW+jHb4iVFR8bLfggSLvSO7LwiVjQWfnQxN+ToLkVBKqMbIENrLUTvynMSEC73xUg==}
@@ -6543,10 +6516,6 @@ packages:
resolution: {integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==}
engines: {node: '>=4.5.0'}
- busboy@1.6.0:
- resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
- engines: {node: '>=10.16.0'}
-
byte-size@8.1.1:
resolution: {integrity: sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==}
engines: {node: '>=12.17'}
@@ -7775,10 +7744,6 @@ packages:
graphile-config: ^1.0.0-rc.5
graphql: 16.13.0
- graphile-config@1.0.0:
- resolution: {integrity: sha512-nPKrrpmYT/cMibqHnNL+zLIRrC/SQhop7yV4tZiyrC/C0mckdlghRWR9oPV7UppkeFIdgTt5/7UQCrwhX82faQ==}
- engines: {node: '>=22'}
-
graphile-config@1.0.1:
resolution: {integrity: sha512-sVdSWNmetW/WZKVQ0Dii2kCu0Le6X6qwuBRecWg575iOMjbZgxo+b4oVeSOTtR6NTKuLsMYQBkzSaeoAKOPB+A==}
engines: {node: '>=22'}
@@ -8672,10 +8637,6 @@ packages:
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
- media-typer@0.3.0:
- resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
- engines: {node: '>= 0.6'}
-
media-typer@1.1.0:
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
engines: {node: '>= 0.8'}
@@ -8969,10 +8930,6 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
- multer@2.1.1:
- resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==}
- engines: {node: '>= 10.16.0'}
-
multimatch@5.0.0:
resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==}
engines: {node: '>=10'}
@@ -9496,27 +9453,6 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
- postgraphile@5.0.0:
- resolution: {integrity: sha512-BEv+qrOQBgMtzq3xWhHzwD9+cTdFz8XG/x7e4qWd/4+oqIDrKzOX+OXhFW8vx7jeRnvCMQ3Y8DYe/4tS4DJjqA==}
- engines: {node: '>=22'}
- hasBin: true
- peerDependencies:
- '@dataplan/json': 1.0.0
- '@dataplan/pg': ^1.0.0
- '@envelop/core': ^5.0.0
- grafast: ^1.0.0
- grafserv: ^1.0.0
- graphile-build: ^5.0.0
- graphile-build-pg: ^5.0.0
- graphile-config: ^1.0.0
- graphql: 16.13.0
- pg: ^8.7.1
- pg-sql2: ^5.0.0
- tamedevil: ^0.1.0
- peerDependenciesMeta:
- '@envelop/core':
- optional: true
-
postgraphile@5.0.3:
resolution: {integrity: sha512-gO8xOusrIykV65V1rmkqnUz3ZjDU+1y09uWruUshP+t811JFsEZsE7d3UJNjAxgrBcndU0CzgmEWCbvbD3kehA==}
engines: {node: '>=22'}
@@ -10157,10 +10093,6 @@ packages:
resolution: {integrity: sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==}
engines: {node: '>=0.8.0'}
- streamsearch@1.1.0:
- resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
- engines: {node: '>=10.0.0'}
-
strfy-js@3.2.2:
resolution: {integrity: sha512-hUgJ5k2PR1ivhq4uObxnin5j6GcOr0Y0N1lzi3z6SRhxNqu4rzpDfyoC2ToUAyM8yXNXM0zs6f4KIiqj8NqheQ==}
@@ -10453,10 +10385,6 @@ packages:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
- type-is@1.6.18:
- resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
- engines: {node: '>= 0.6'}
-
type-is@2.0.1:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
engines: {node: '>= 0.6'}
@@ -11453,26 +11381,6 @@ snapshots:
grafast: 1.0.2(graphql@16.13.0)
tslib: 2.8.1
- '@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)':
- dependencies:
- '@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
- '@graphile/lru': 5.0.0
- '@types/node': 22.19.19
- chalk: 4.1.2
- debug: 4.4.3(supports-color@5.5.0)
- eventemitter3: 5.0.4
- grafast: 1.0.2(graphql@16.13.0)
- graphile-config: 1.0.0
- graphql: 16.13.0
- pg-sql2: 5.0.1
- postgres-array: 3.0.4
- postgres-range: 1.1.4
- tslib: 2.8.1
- optionalDependencies:
- pg: 8.21.0
- transitivePeerDependencies:
- - supports-color
-
'@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)':
dependencies:
'@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
@@ -13733,10 +13641,6 @@ snapshots:
'@types/http-errors@2.0.5': {}
- '@types/interpret@1.1.4':
- dependencies:
- '@types/node': 22.19.19
-
'@types/istanbul-lib-coverage@2.0.6': {}
'@types/istanbul-lib-report@3.0.3':
@@ -13790,10 +13694,6 @@ snapshots:
'@types/minimist@1.2.5': {}
- '@types/multer@2.1.0':
- dependencies:
- '@types/express': 5.0.6
-
'@types/node@22.19.11':
dependencies:
undici-types: 6.21.0
@@ -14164,8 +14064,6 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.1
- append-field@1.0.0: {}
-
appstash@0.7.0: {}
aproba@2.0.0: {}
@@ -14407,10 +14305,6 @@ snapshots:
dependencies:
dicer: 0.3.0
- busboy@1.6.0:
- dependencies:
- streamsearch: 1.1.0
-
byte-size@8.1.1: {}
bytes@3.1.2: {}
@@ -15680,31 +15574,6 @@ snapshots:
- supports-color
- use-sync-external-store
- grafserv@1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1):
- dependencies:
- '@graphile/lru': 5.0.0
- debug: 4.4.3(supports-color@5.5.0)
- eventemitter3: 5.0.4
- grafast: 1.0.2(graphql@16.13.0)
- graphile-config: 1.0.0
- graphql: 16.13.0
- graphql-ws: 6.0.8(graphql@16.13.0)(ws@8.20.1)
- ruru: 2.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(debug@4.4.3)(graphile-config@1.0.0)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))
- tslib: 2.8.1
- optionalDependencies:
- ws: 8.20.1
- transitivePeerDependencies:
- - '@fastify/websocket'
- - '@types/node'
- - '@types/react'
- - '@types/react-dom'
- - crossws
- - immer
- - react
- - react-dom
- - supports-color
- - use-sync-external-store
-
grafserv@1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1):
dependencies:
'@graphile/lru': 5.0.0
@@ -15730,25 +15599,6 @@ snapshots:
- supports-color
- use-sync-external-store
- graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1):
- dependencies:
- '@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)
- '@types/node': 22.19.19
- debug: 4.4.3(supports-color@5.5.0)
- grafast: 1.0.2(graphql@16.13.0)
- graphile-build: 5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)
- graphile-config: 1.0.0
- graphql: 16.13.0
- jsonwebtoken: 9.0.3
- pg-introspection: 1.0.1
- pg-sql2: 5.0.1
- tamedevil: 0.1.1
- tslib: 2.8.1
- optionalDependencies:
- pg: 8.21.0
- transitivePeerDependencies:
- - supports-color
-
graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)(tamedevil@0.1.1):
dependencies:
'@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)
@@ -15787,25 +15637,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0):
- dependencies:
- '@types/node': 22.19.19
- '@types/pluralize': 0.0.33
- '@types/semver': 7.7.1
- chalk: 4.1.2
- debug: 4.4.3(supports-color@5.5.0)
- grafast: 1.0.2(graphql@16.13.0)
- graphile-config: 1.0.0
- graphql: 16.13.0
- lodash: 4.18.1
- pluralize: 7.0.0
- semver: 7.8.1
- tamedevil: 0.1.1
- transliteration: 2.6.1
- tslib: 2.8.1
- transitivePeerDependencies:
- - supports-color
-
graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0):
dependencies:
'@types/node': 22.19.19
@@ -15825,20 +15656,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- graphile-config@1.0.0:
- dependencies:
- '@types/interpret': 1.1.4
- '@types/node': 22.19.19
- '@types/semver': 7.7.1
- chalk: 4.1.2
- debug: 4.4.3(supports-color@5.5.0)
- interpret: 3.1.1
- semver: 7.8.1
- tslib: 2.8.1
- yargs: 17.7.2
- transitivePeerDependencies:
- - supports-color
-
graphile-config@1.0.1:
dependencies:
chalk: 4.1.2
@@ -15866,22 +15683,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- graphile-utils@5.0.1(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(tamedevil@0.1.1):
- dependencies:
- '@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)
- debug: 4.4.3(supports-color@5.5.0)
- grafast: 1.0.2(graphql@16.13.0)
- graphile-build: 5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)
- graphile-config: 1.0.0
- graphql: 16.13.0
- json5: 2.2.3
- tamedevil: 0.1.1
- tslib: 2.8.1
- optionalDependencies:
- graphile-build-pg: 5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1)
- transitivePeerDependencies:
- - supports-color
-
graphile-utils@5.0.1(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)(tamedevil@0.1.1))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(tamedevil@0.1.1):
dependencies:
'@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.1)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.20.0)
@@ -17193,8 +16994,6 @@ snapshots:
mdurl@2.0.0: {}
- media-typer@0.3.0: {}
-
media-typer@1.1.0: {}
mensch@0.3.4: {}
@@ -17668,13 +17467,6 @@ snapshots:
ms@2.1.3: {}
- multer@2.1.1:
- dependencies:
- append-field: 1.0.0
- busboy: 1.6.0
- concat-stream: 2.0.0
- type-is: 1.6.18
-
multimatch@5.0.0:
dependencies:
'@types/minimatch': 3.0.5
@@ -18285,33 +18077,6 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
- postgraphile@5.0.0(f3ea149703b7f495547261dad1e3b475):
- dependencies:
- '@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
- '@dataplan/pg': 1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)
- '@graphile/lru': 5.0.0
- '@types/node': 22.19.19
- '@types/pg': 8.20.0
- debug: 4.4.3(supports-color@5.5.0)
- grafast: 1.0.2(graphql@16.13.0)
- grafserv: 1.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))(ws@8.20.1)
- graphile-build: 5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)
- graphile-build-pg: 5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1)
- graphile-config: 1.0.0
- graphile-utils: 5.0.1(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build-pg@5.0.2(@dataplan/pg@1.0.3(@dataplan/json@1.0.0(grafast@1.0.2(graphql@16.13.0)))(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0))(grafast@1.0.2(graphql@16.13.0))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(pg-sql2@5.0.1)(pg@8.21.0)(tamedevil@0.1.1))(graphile-build@5.0.2(grafast@1.0.2(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0))(graphile-config@1.0.0)(graphql@16.13.0)(tamedevil@0.1.1)
- graphql: 16.13.0
- iterall: 1.3.0
- jsonwebtoken: 9.0.3
- pg: 8.21.0
- pg-sql2: 5.0.1
- tamedevil: 0.1.1
- tslib: 2.8.1
- ws: 8.20.1
- transitivePeerDependencies:
- - bufferutil
- - supports-color
- - utf-8-validate
-
postgraphile@5.0.3(02c18e7c6179c4ae54031f8cdca16ab5):
dependencies:
'@dataplan/json': 1.0.0(grafast@1.0.2(graphql@16.13.0))
@@ -18942,27 +18707,6 @@ snapshots:
- immer
- use-sync-external-store
- ruru@2.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(debug@4.4.3)(graphile-config@1.0.0)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)):
- dependencies:
- '@emotion/is-prop-valid': 1.4.0
- graphile-config: 1.0.0
- graphql: 16.13.0
- http-proxy: 1.18.1(debug@4.4.3)
- ruru-types: 2.0.0(@emotion/is-prop-valid@1.4.0)(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))
- tslib: 2.8.1
- yargs: 17.7.2
- optionalDependencies:
- react: 19.2.5
- react-dom: 19.2.5(react@19.2.5)
- transitivePeerDependencies:
- - '@types/node'
- - '@types/react'
- - '@types/react-dom'
- - debug
- - graphql-ws
- - immer
- - use-sync-external-store
-
ruru@2.0.0(@types/node@25.9.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(debug@4.4.3)(graphile-config@1.0.1)(graphql-ws@6.0.8(graphql@16.13.0)(ws@8.20.1))(graphql@16.13.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)):
dependencies:
'@emotion/is-prop-valid': 1.4.0
@@ -19208,8 +18952,6 @@ snapshots:
streamsearch@0.1.2: {}
- streamsearch@1.1.0: {}
-
strfy-js@3.2.2:
dependencies:
minimatch: 10.2.5
@@ -19532,11 +19274,6 @@ snapshots:
type-fest@4.41.0: {}
- type-is@1.6.18:
- dependencies:
- media-typer: 0.3.0
- mime-types: 2.1.35
-
type-is@2.0.1:
dependencies:
content-type: 1.0.5
From b670d413a066b96b9cba39151c13b6f92214348b Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Wed, 17 Jun 2026 11:54:04 +0800
Subject: [PATCH 10/17] rerun perf e2e test
---
graphql/server/perf/e2e-benchmark.ts | 51 +++++++++++++++++++++++-
graphql/server/perf/run-comparison.sh | 3 +-
graphql/server/perf/run-e2e-benchmark.sh | 3 +-
3 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/graphql/server/perf/e2e-benchmark.ts b/graphql/server/perf/e2e-benchmark.ts
index 6737a748e2..c0100d7af9 100644
--- a/graphql/server/perf/e2e-benchmark.ts
+++ b/graphql/server/perf/e2e-benchmark.ts
@@ -20,6 +20,11 @@ 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';
@@ -142,6 +147,24 @@ async function getHeapUsedMB(): Promise {
// ─── 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}`;
@@ -159,6 +182,26 @@ function buildTenantProfiles(k: number): TenantProfile[] {
// ─── 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',
@@ -270,6 +313,10 @@ async function runBenchmark(): Promise {
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);
@@ -327,7 +374,9 @@ async function runBenchmark(): Promise {
for (const r of results) {
totalQueries += r.totalQueries;
totalErrors += r.errors;
- allLatencies.push(...r.latencies);
+ for (const latency of r.latencies) {
+ allLatencies.push(latency);
+ }
allErrorSamples.push(...r.errorSamples);
}
diff --git a/graphql/server/perf/run-comparison.sh b/graphql/server/perf/run-comparison.sh
index 845ab50523..5ca5e6fed7 100755
--- a/graphql/server/perf/run-comparison.sh
+++ b/graphql/server/perf/run-comparison.sh
@@ -64,12 +64,13 @@ export no_proxy="${no_proxy:-$NO_PROXY}"
export NODE_ENV=development
export GRAPHILE_ENV=development
export GRAPHQL_OBSERVABILITY_ENABLED=true
-export API_IS_PUBLIC=false
+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 "=============================================================="
diff --git a/graphql/server/perf/run-e2e-benchmark.sh b/graphql/server/perf/run-e2e-benchmark.sh
index dbe682f850..19bd757eb6 100755
--- a/graphql/server/perf/run-e2e-benchmark.sh
+++ b/graphql/server/perf/run-e2e-benchmark.sh
@@ -44,12 +44,13 @@ export no_proxy="${no_proxy:-$NO_PROXY}"
export NODE_ENV=development
export GRAPHILE_ENV=development
export GRAPHQL_OBSERVABILITY_ENABLED=true
-export API_IS_PUBLIC=false
+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() {
From 9e88b257a760a6dcf71f35875101e9a34a6b588a Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Thu, 18 Jun 2026 16:35:57 +0800
Subject: [PATCH 11/17] refactor graphile multi tenancy cache plugin
---
.../graphile-multi-tenancy-cache/README.md | 52 +-
.../src/__tests__/buildkey.test.ts | 2 +-
.../src/build-key.ts | 95 ++++
.../src/cache-runtime.ts | 234 ++++++++
.../src/lifecycle.ts | 35 ++
.../src/multi-tenancy-cache.ts | 531 ++----------------
.../src/tenant-indexes.ts | 76 +++
.../graphile-multi-tenancy-cache/src/types.ts | 70 +++
graphql/server/src/middleware/graphile.ts | 2 +-
9 files changed, 579 insertions(+), 518 deletions(-)
create mode 100644 graphile/graphile-multi-tenancy-cache/src/build-key.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/cache-runtime.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/lifecycle.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/tenant-indexes.ts
create mode 100644 graphile/graphile-multi-tenancy-cache/src/types.ts
diff --git a/graphile/graphile-multi-tenancy-cache/README.md b/graphile/graphile-multi-tenancy-cache/README.md
index caafb36a39..253cb98390 100644
--- a/graphile/graphile-multi-tenancy-cache/README.md
+++ b/graphile/graphile-multi-tenancy-cache/README.md
@@ -39,7 +39,7 @@ npm install graphile-multi-tenancy-cache
## Usage
-This package is a runtime orchestrator, not a schema plugin. You configure it with a preset builder, then resolve handlers per request.
+This package is a runtime orchestrator, not a schema plugin. You configure it with a handler factory, then resolve handlers per request.
```typescript
import {
@@ -51,14 +51,26 @@ import {
} from 'graphile-multi-tenancy-cache';
configureMultiTenancyCache({
- basePresetBuilder(pool, schemas, anonRole, roleName) {
- return {
- extends: [],
- grafast: {
- context: () => ({})
- },
- pgServices: [],
- };
+ async handlerFactory({
+ buildKey,
+ svcKey,
+ pool,
+ schemas,
+ anonRole,
+ roleName,
+ databaseId,
+ presetOptions,
+ }) {
+ return createTenantGraphileResources({
+ pool,
+ schemas,
+ anonRole,
+ roleName,
+ databaseSettings: presetOptions,
+ cacheKey: buildKey,
+ serviceKey: svcKey,
+ databaseId,
+ });
},
});
@@ -74,6 +86,7 @@ async function handleGraphql(req, res) {
anonRole: req.api.anonRole,
roleName: req.api.roleName,
databaseId: req.api.databaseId,
+ presetOptions: req.api.databaseSettings,
});
}
@@ -87,7 +100,7 @@ process.on('SIGTERM', async () => {
## Features
-- **Exact-match buildKey reuse** — handlers are shared only when connection identity, schema set, and role inputs match exactly
+- **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
@@ -111,6 +124,7 @@ process.on('SIGTERM', async () => {
- schema list
- `anonRole`
- `roleName`
+- `presetOptions`, when supplied
It does **not** include:
@@ -125,10 +139,10 @@ Schema order is preserved. `['a', 'b']` and `['b', 'a']` intentionally produce d
Examples:
-- A buildKey is a canonical string derived from connection identity, schemas, and role inputs:
+- 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"}
+{"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:
@@ -146,11 +160,12 @@ svc_key: schemata:main-db:services_public
- `schemas`
- `anonRole`
- `roleName`
+ - `presetOptions`
These then feed into the buildKey:
```json
-{"conn":":/@","schemas":[...],"anonRole":"...","roleName":"..."}
+{"conn":":/@","schemas":[...],"anonRole":"...","roleName":"...","presetOptions":{...}}
```
Different route keys only share a `buildKey` if they ultimately resolve to the same:
@@ -159,6 +174,7 @@ svc_key: schemata:main-db:services_public
- `schemas`
- `anonRole`
- `roleName`
+ - `presetOptions`
In practice, the resolution rules differ by path:
@@ -172,8 +188,8 @@ svc_key: schemata:main-db:services_public
- 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"}
-{"conn":"127.0.0.1:5432/mydb@postgres","schemas":["metaschema_public","services_public"],"anonRole":"administrator","roleName":"administrator"}
+{"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.
@@ -188,7 +204,7 @@ At runtime the cache maintains three main indexes:
The flow is:
-1. Compute the `buildKey` from pool identity, schemas, and role inputs.
+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.
@@ -217,14 +233,14 @@ The package supports:
| Export | Purpose |
|--------|---------|
-| `configureMultiTenancyCache(config)` | Registers the base preset builder. Must be called before handler creation. |
+| `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)` | Compute the exact-match handler identity. |
+| `computeBuildKey(pool, schemas, anonRole, roleName, presetOptions?)` | Compute the exact-match handler identity. |
| `getBuildKeyForSvcKey(svcKey)` | Resolve the buildKey currently mapped to a route key. |
## License
diff --git a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
index c8424e086c..8c3ed53dbb 100644
--- a/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/__tests__/buildkey.test.ts
@@ -763,7 +763,7 @@ describe('coalesced creation failure — no orphaned mappings (Finding 1)', () =
});
it('should preserve all svc_key mappings when coalesced creation succeeds', async () => {
- // Uses the default (working) preset builder from beforeEach
+ // 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' };
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/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
index cd73761552..a45788f3bc 100644
--- a/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
+++ b/graphile/graphile-multi-tenancy-cache/src/multi-tenancy-cache.ts
@@ -16,454 +16,44 @@
* databaseIdToBuildKeys: databaseId → Set
*/
-import { Logger } from '@pgpmjs/logger';
-import type { Express } from 'express';
-import type { Server as HttpServer } from 'node:http';
-import type { Pool } from 'pg';
-import { LRUCache } from 'lru-cache';
+import { computeBuildKey } from './build-key';
+import { CacheRuntime } from './cache-runtime';
+import type {
+ MultiTenancyCacheConfig,
+ MultiTenancyCacheStats,
+ TenantConfig,
+ TenantInstance,
+} from './types';
-const log = new Logger('multi-tenancy-cache');
-
-// --- Types ---
-
-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[];
-}
-
-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;
-
-// --- Internal state ---
-
-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;
-
-const getHandlerCacheConfig = () => {
- 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,
- ),
- };
-};
-
-const initialCacheConfig = getHandlerCacheConfig();
-
-/** Tenant resources that have already been disposed. Prevents double release via manual + LRU paths. */
-const disposedTenants = new WeakSet();
-
-/** buildKey → TenantInstance (the real handler cache) */
-const handlerCache = new LRUCache({
- max: initialCacheConfig.max,
- ttl: initialCacheConfig.ttlMs,
- updateAgeOnGet: true,
- dispose: (handler, buildKey) => {
- removeIndexesForBuildKey(buildKey);
- disposeTenant(handler).catch((err) => {
- log.error(`Failed to dispose handler buildKey=${buildKey}:`, err);
- });
- },
-});
-
-/** svc_key → buildKey (routing index) */
-const svcKeyToBuildKey = new Map();
-
-/** databaseId → Set (flush-by-database index) */
-const databaseIdToBuildKeys = new Map>();
-
-/** buildKey → Promise (single-flight coalescing) */
-const creatingHandlers = new Map>();
-
-/**
- * Per-svc_key monotonic epoch.
- *
- * Each call to getOrCreateTenantInstance increments the epoch for its
- * svc_key. After handler creation completes, the caller only registers
- * a mapping if its captured epoch still matches the current value.
- *
- * This prevents a stale (older, slower) build from overwriting a
- * newer binding that completed earlier.
- */
-const svcKeyEpoch = new Map();
-
-/** The handler factory, set once by configureMultiTenancyCache(). */
-let handlerFactory: TenantHandlerFactory | null = null;
-
-// --- Configuration ---
-
-export interface MultiTenancyCacheConfig {
- handlerFactory: TenantHandlerFactory;
-}
+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 {
- handlerFactory = config.handlerFactory;
- log.info('Multi-tenancy cache configured (buildKey-based handler caching)');
-}
-
-// --- BuildKey computation ---
-
-/**
- * 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)
- */
-function getPoolIdentity(pool: Pool): string {
- const opts = (pool as unknown as { options: Record }).options || {};
-
- // Primary path: parse connectionString (matches real pg-cache pool shape)
- 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 {
- // If URL parsing fails, use the raw connectionString (will be hashed anyway)
- return opts.connectionString;
- }
- }
-
- // Fallback: individual fields (for pools created with explicit host/database/user)
- if (opts.host || opts.database || opts.user) {
- return `${opts.host || 'localhost'}:${opts.port || 5432}/${opts.database || ''}@${opts.user || ''}`;
- }
-
- // Last resort: no identity available — log a warning
- log.warn('Pool has no connectionString or individual connection fields — buildKey may not be unique');
- return 'unknown-pool';
-}
-
-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;
+ store.configure(config);
}
-/**
- * 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
- *
- * Does NOT include:
- * - svc_key (routing-only)
- * - databaseId (metadata-only)
- * - token data, host/domain, transient headers
- *
- * The buildKey is intentionally stored as a canonical plain-text string
- * rather than a truncated hash so there is no collision risk between
- * different tenant build inputs.
- */
-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);
-}
-
-// --- Index management ---
-
-/**
- * Register a svc_key → buildKey mapping and update the databaseId index.
- *
- * If the svc_key was previously mapped to a different buildKey, the old
- * mapping is cleaned up first. If no other svc_keys still reference the
- * old buildKey, it is evicted (handler disposed, indexes purged).
- */
-function registerMapping(svcKey: string, buildKey: string, databaseId?: string): void {
- const oldBuildKey = svcKeyToBuildKey.get(svcKey);
-
- if (oldBuildKey && oldBuildKey !== buildKey) {
- // Rebinding — detach this svc_key from the old buildKey first
- svcKeyToBuildKey.delete(svcKey);
-
- // If old buildKey has no remaining svc_key references, evict it
- if (getSvcKeysForBuildKey(oldBuildKey).length === 0) {
- evictBuildKey(oldBuildKey);
- }
- }
-
- svcKeyToBuildKey.set(svcKey, buildKey);
- if (databaseId) {
- let keys = databaseIdToBuildKeys.get(databaseId);
- if (!keys) {
- keys = new Set();
- databaseIdToBuildKeys.set(databaseId, keys);
- }
- keys.add(buildKey);
- }
-}
-
-/**
- * Collect all svc_keys that map to a given buildKey.
- */
-function getSvcKeysForBuildKey(buildKey: string): string[] {
- const result: string[] = [];
- for (const [svcKey, bk] of svcKeyToBuildKey) {
- if (bk === buildKey) result.push(svcKey);
- }
- return result;
-}
-
-function removeIndexesForBuildKey(buildKey: string): void {
- for (const svcKey of getSvcKeysForBuildKey(buildKey)) {
- svcKeyToBuildKey.delete(svcKey);
- }
-
- for (const [dbId, keys] of databaseIdToBuildKeys) {
- keys.delete(buildKey);
- if (keys.size === 0) databaseIdToBuildKeys.delete(dbId);
- }
-}
-
-/**
- * Remove a buildKey from all indexes and dispose the handler (if present).
- *
- * Also cleans orphaned indexes — if handler creation failed, the handler
- * won't be in handlerCache but the svc_key and databaseId indexes may
- * still reference the buildKey. This function cleans those too.
- */
-function evictBuildKey(buildKey: string): void {
- const deleted = handlerCache.delete(buildKey);
- if (!deleted) {
- removeIndexesForBuildKey(buildKey);
- }
-
- log.debug(`Evicted buildKey=${buildKey} (handler=${deleted ? 'disposed' : 'none/orphaned'})`);
-}
-
-// --- Core API ---
-
/**
* Fast-path lookup: svc_key → buildKey → handler.
*/
export function getTenantInstance(svcKey: string): TenantInstance | undefined {
- const buildKey = svcKeyToBuildKey.get(svcKey);
- if (!buildKey) return undefined;
-
- const handler = handlerCache.get(buildKey);
- if (handler) {
- handler.lastUsedAt = Date.now();
- }
- return handler;
+ return store.getTenantInstance(svcKey);
}
/**
* Resolve the buildKey for a given svc_key (for diagnostics / external use).
*/
export function getBuildKeyForSvcKey(svcKey: string): string | undefined {
- return svcKeyToBuildKey.get(svcKey);
+ return store.getBuildKeyForSvcKey(svcKey);
}
/**
* Resolve or create a tenant handler.
- *
- * Flow:
- * 1. Compute buildKey from config's build inputs
- * 2. Check handlerCache (fast path) → register mapping, return
- * 3. Check creatingHandlers (single-flight coalesce) → await, register on success
- * 4. Create a new independent handler keyed by buildKey
- * 5. On success: register mapping, store in handlerCache, return
- *
- * Registration is deferred until AFTER handler creation succeeds. This
- * ensures that if the shared in-flight promise rejects, NO participating
- * svc_key leaves orphaned mappings — creator or follower alike.
*/
-export async function getOrCreateTenantInstance(
- config: TenantConfig,
-): Promise {
- const { svcKey, pool, schemas, anonRole, roleName, databaseId, presetOptions } = config;
-
- if (!handlerFactory) {
- throw new Error('Multi-tenancy cache not configured. Call configureMultiTenancyCache() first.');
- }
-
- const buildKey = computeBuildKey(pool, schemas, anonRole, roleName, presetOptions);
-
- // Capture a monotonically increasing epoch for this svc_key.
- // Only the request holding the latest epoch is allowed to register.
- const epoch = (svcKeyEpoch.get(svcKey) ?? 0) + 1;
- svcKeyEpoch.set(svcKey, epoch);
-
- // Step 1: Fast path — handler already cached
- const existing = handlerCache.get(buildKey);
- if (existing) {
- existing.lastUsedAt = Date.now();
- if (svcKeyEpoch.get(svcKey) === epoch) {
- registerMapping(svcKey, buildKey, databaseId);
- }
- return existing;
- }
-
- // Step 2: Single-flight coalesce — handler being created by another request
- const pending = creatingHandlers.get(buildKey);
- if (pending) {
- // Await the shared promise; register only on success
- const result = await pending;
- if (svcKeyEpoch.get(svcKey) === epoch) {
- registerMapping(svcKey, buildKey, databaseId);
- }
- return result;
- }
-
- // Step 3: Creator path — build a new handler
- const promise = doCreateHandler({
- buildKey,
- svcKey,
- pool,
- schemas,
- anonRole,
- roleName,
- databaseId,
- presetOptions,
- });
- creatingHandlers.set(buildKey, promise);
-
- try {
- const result = await promise;
- if (svcKeyEpoch.get(svcKey) === epoch) {
- registerMapping(svcKey, buildKey, databaseId);
- } else {
- // Stale completion — a newer request for this svc_key superseded us.
- // Defer the orphan check so coalesced followers from OTHER svc_keys
- // have a chance to register before we decide the buildKey is orphaned.
- queueMicrotask(() => {
- if (getSvcKeysForBuildKey(buildKey).length === 0) {
- evictBuildKey(buildKey);
- }
- });
- }
- return result;
- } finally {
- creatingHandlers.delete(buildKey);
- }
-}
-
-async function doCreateHandler(
- context: TenantHandlerFactoryContext,
-): Promise {
- const { buildKey, schemas } = context;
- const schemaLabel = schemas.join(',') || 'unknown';
-
- log.info(`Building handler buildKey=${buildKey} schemas=${schemaLabel}`);
-
- const resources = await handlerFactory!(context);
-
- const tenant: TenantInstance = {
- ...resources,
- buildKey,
- schemas,
- createdAt: Date.now(),
- lastUsedAt: Date.now(),
- };
-
- handlerCache.set(buildKey, tenant);
-
- log.info(`Handler created buildKey=${buildKey} schemas=${schemaLabel}`);
- return tenant;
+export function getOrCreateTenantInstance(config: TenantConfig): Promise {
+ return store.getOrCreateTenantInstance(config);
}
/**
@@ -474,94 +64,39 @@ async function doCreateHandler(
* re-create it on next request.
*/
export function flushTenantInstance(svcKey: string): void {
- const buildKey = svcKeyToBuildKey.get(svcKey);
- if (!buildKey) return;
-
- evictBuildKey(buildKey);
- log.debug(`Flushed via svc_key=${svcKey} → buildKey=${buildKey}`);
+ store.flushTenantInstance(svcKey);
}
/**
* Flush all handlers associated with a databaseId.
*/
export function flushByDatabaseId(databaseId: string): void {
- const buildKeys = databaseIdToBuildKeys.get(databaseId);
- if (!buildKeys || buildKeys.size === 0) return;
-
- // Copy to avoid mutation during iteration
- const keysToEvict = [...buildKeys];
- for (const buildKey of keysToEvict) {
- evictBuildKey(buildKey);
- }
-
- // Clean up the databaseId entry (evictBuildKey already removes individual keys)
- databaseIdToBuildKeys.delete(databaseId);
-
- log.debug(`Flushed ${keysToEvict.length} handler(s) for databaseId=${databaseId}`);
-}
-
-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);
- }
+ store.flushByDatabaseId(databaseId);
}
/**
* Get diagnostic stats for the multi-tenancy cache system.
*/
export function getMultiTenancyCacheStats(): MultiTenancyCacheStats {
- return {
- handlerCacheSize: handlerCache.size,
- handlerCacheMax: initialCacheConfig.max,
- handlerCacheTtlMs: initialCacheConfig.ttlMs,
- svcKeyMappings: svcKeyToBuildKey.size,
- databaseIdMappings: databaseIdToBuildKeys.size,
- inflightCreations: creatingHandlers.size,
- buildKeys: [...handlerCache.keys()],
- };
+ return store.getStats();
}
/**
* Release all resources — handler cache, indexes, and in-flight trackers.
*/
-export async function shutdownMultiTenancyCache(): Promise {
- log.info('Shutting down multi-tenancy cache...');
-
- // Dispose all cached handlers
- const disposals: Promise[] = [];
- for (const handler of handlerCache.values()) {
- disposals.push(disposeTenant(handler));
- }
- await Promise.allSettled(disposals);
-
- // Clear all state
- handlerCache.clear();
- svcKeyToBuildKey.clear();
- databaseIdToBuildKeys.clear();
- creatingHandlers.clear();
- svcKeyEpoch.clear();
- handlerFactory = null;
-
- log.info('Multi-tenancy cache shutdown complete');
-}
+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/graphql/server/src/middleware/graphile.ts b/graphql/server/src/middleware/graphile.ts
index a1724a4523..e2e5523f0f 100644
--- a/graphql/server/src/middleware/graphile.ts
+++ b/graphql/server/src/middleware/graphile.ts
@@ -498,7 +498,7 @@ export async function shutdownMultiTenancy(): Promise {
export const multiTenancyHandler = (opts: ConstructiveOptions): RequestHandler => {
const observabilityEnabled = isGraphqlObservabilityEnabled(opts.server?.host);
- // One-time bootstrap: configure the multi-tenancy cache with our preset builder
+ // One-time bootstrap: configure the multi-tenancy cache with our handler factory
configureMultiTenancyCache({
handlerFactory: async ({
buildKey,
From 90941c1b97825d6dd97195bf61bd7b2c0f3551e5 Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Fri, 19 Jun 2026 10:47:40 +0800
Subject: [PATCH 12/17] lock file
---
pnpm-lock.yaml | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9642d096ab..990d54cfcd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1676,9 +1676,6 @@ importers:
graphql/server:
dependencies:
- '@agentic-kit/ollama':
- specifier: workspace:*
- version: link:../../agentic/ollama/dist
'@constructive-io/csrf':
specifier: workspace:^
version: link:../../packages/csrf/dist
@@ -1712,6 +1709,9 @@ importers:
'@pgpmjs/types':
specifier: workspace:^
version: link:../../pgpm/types/dist
+ agentic-server:
+ specifier: workspace:*
+ version: link:../../agentic/agentic-server/dist
cors:
specifier: ^2.8.6
version: 2.8.6
@@ -1742,9 +1742,6 @@ importers:
graphile-config:
specifier: 1.0.1
version: 1.0.1
- graphile-llm:
- specifier: workspace:^
- version: link:../../graphile/graphile-llm/dist
graphile-multi-tenancy-cache:
specifier: workspace:^
version: link:../../graphile/graphile-multi-tenancy-cache/dist
From c7dd63e51a01db89fa9f58b7482467323ad1e24c Mon Sep 17 00:00:00 2001
From: Zhi Zhen
Date: Thu, 25 Jun 2026 15:23:45 +0800
Subject: [PATCH 13/17] docs(perf): consolidate perf README around TS CLI
target
---
graphql/server/perf/E2E_BENCHMARK_REPORT.md | 148 -----
graphql/server/perf/README.md | 550 +++++++++++++++---
.../perf/REAL_MULTITENANT_E2E_CURRENT.md | 209 -------
3 files changed, 453 insertions(+), 454 deletions(-)
delete mode 100644 graphql/server/perf/E2E_BENCHMARK_REPORT.md
delete mode 100644 graphql/server/perf/REAL_MULTITENANT_E2E_CURRENT.md
diff --git a/graphql/server/perf/E2E_BENCHMARK_REPORT.md b/graphql/server/perf/E2E_BENCHMARK_REPORT.md
deleted file mode 100644
index bfac913a56..0000000000
--- a/graphql/server/perf/E2E_BENCHMARK_REPORT.md
+++ /dev/null
@@ -1,148 +0,0 @@
-# Multi-Tenancy Cache Stress Test Report
-
-This document captures the stress-test results for the current multi-tenancy caching strategy.
-
-The optimization logic evaluated here is:
-
-- exact-match buildKey handler reuse
-- perf stress testing of the old vs new request path
-
-## Test Configuration
-
-| Parameter | Value |
-|---|---|
-| **Server** | Constructive GraphQL server |
-| **Database** | PostgreSQL on localhost:5432, `postgres_perf` |
-| **Node.js** | `NODE_ENV=development` |
-| **Mode comparison** | OLD = dedicated instances (`GRAPHILE_CACHE_MAX` tuned); NEW = `USE_MULTI_TENANCY_CACHE=true` |
-| **Benchmark tool** | `e2e-benchmark.ts` through Express -> PostGraphile -> Grafast -> PostgreSQL |
-| **Query mix** | ListApis (40%), ListApps (20%), ListDomains (20%), Introspection (10%), MetaQuery (10%) |
-
-## Stress Matrix
-
-| # | Test | K | Workers | Duration | Schema Variants | Extras |
-|---|------|---|---------|----------|-----------------|--------|
-| 1 | High-K scale | 100 | 10 | 5 min | `SCHEMA_VARIANTS=4` | `MULTI_ENDPOINT=true` |
-| 2 | High concurrency | 30 | 10 | 5 min | `SCHEMA_VARIANTS=4` | `MULTI_ENDPOINT=true` |
-| 3 | Flush under load | 30 | 10 | 5 min | `SCHEMA_VARIANTS=4` | `CHAOS_FLUSH=true FLUSH_INTERVAL=30` |
-| 4 | Mixed buildKeys (max divergence) | 30 | 10 | 5 min | `SCHEMA_VARIANTS=8` | — |
-| 5 | Soak | 30 | 10 | 2 hr | `SCHEMA_VARIANTS=4` | `CHAOS_FLUSH=true FLUSH_INTERVAL=60` |
-| 6 | Startup burst | 30 | 10 | 1 min | `SCHEMA_VARIANTS=4` | `BURST_START=true` |
-
-Each test was run in OLD mode and NEW mode.
-
-## Results Summary
-
-### Throughput and Memory
-
-| # | Test | OLD QPS | NEW QPS | QPS Delta | OLD Heap Delta | NEW Heap Delta | Heap Saved | svc_keys | Errors |
-|---|------|---------|---------|-----------|----------------|----------------|------------|----------|--------|
-| 1 | High-K | 697 | 772 | +11% | 1,502 MB | 191 MB | -87% | 400 | 0 |
-| 2 | High concurrency | 717 | 731 | +2% | 648 MB | 262 MB | -60% | 120 | 0 |
-| 3 | Flush under load | 689 | 738 | +7% | 1,019 MB | 220 MB | -78% | 120 | 0 |
-| 4 | Mixed buildKeys | 730 | 760 | +4% | 950 MB | 51 MB | -95% | 240 | 0 |
-| 5 | Soak | 733 | 763 | +4% | 856 MB | 560 MB | -35% | 120 | 0 |
-| 6 | Startup burst | 728 | 746 | +2% | 1,216 MB | 264 MB | -78% | 120 | 0 |
-
-### Latency
-
-| # | Test | OLD p50 | NEW p50 | OLD p95 | NEW p95 | OLD p99 | NEW p99 |
-|---|------|---------|---------|---------|---------|---------|---------|
-| 1 | High-K | 14 ms | 14 ms | 21 ms | 19 ms | 25 ms | 21 ms |
-| 2 | High concurrency | 15 ms | 15 ms | 21 ms | 21 ms | 25 ms | 24 ms |
-| 3 | Flush under load | 15 ms | 14 ms | 23 ms | 21 ms | 30 ms | 25 ms |
-| 4 | Mixed buildKeys | 14 ms | 14 ms | 21 ms | 20 ms | 25 ms | 23 ms |
-| 5 | Soak | 15 ms | 14 ms | 21 ms | 20 ms | 24 ms | 23 ms |
-| 6 | Startup burst | 14 ms | 14 ms | 21 ms | 20 ms | 25 ms | 25 ms |
-
-### Soak Detail
-
-| Metric | OLD | NEW |
-|--------|-----|-----|
-| Total queries | 5,281,481 | 5,493,698 |
-| QPS | 733 | 763 |
-| Errors | 0 | 0 |
-| Chaos flush events | 119 | 119 |
-| Flush errors | 0 | 0 |
-| Avg flush latency | 23 ms | 23 ms |
-
-## Analysis
-
-### Why the heap savings are so large
-
-The dominant effect is exact-match buildKey deduplication.
-
-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 single handler.
-
-Those build inputs are:
-
-- connection identity
-- schema set
-- `anonRole`
-- `roleName`
-
-This means the memory benefit depends on the ratio between:
-
-- total `svc_key`s observed by the benchmark
-- distinct buildKeys that actually need handlers
-
-When many keys collapse onto a small set of buildKeys, heap savings become dramatic.
-
-### Why throughput gains are smaller than heap gains
-
-The benchmark exercises full HTTP traffic through Express, PostGraphile, Grafast, and PostgreSQL. That means request execution is still dominated by network and query work, not only handler reuse.
-
-The new path still wins because:
-
-- fewer handlers means less GC pressure
-- working-set size is smaller under load
-- hot handlers stay reused across many route keys
-
-At higher `K` and higher `svc_key` fanout, these effects become more visible.
-
-### Why the soak run saves less heap than the short runs
-
-The 2-hour soak includes repeated flushes. That means both modes spend time destroying and rebuilding runtime state instead of converging to a steady-state heap profile.
-
-The new mode still uses less memory, but the gap narrows because:
-
-- handlers are repeatedly evicted
-- rebuilt handlers temporarily increase active memory
-- steady-state reuse is interrupted by churn
-
-### Stability under concurrency and churn
-
-The stress runs validate the runtime fixes added around the buildKey path:
-
-- deferred registration prevents failed in-flight creation from leaving orphaned mappings
-- rebinding cleanup prevents stale buildKey entries from becoming unreachable leaks
-- the `svc_key` epoch guard prevents stale completions from overwriting newer bindings
-
-The 2-hour soak is the strongest signal here: millions of queries, repeated flushes, and no observed request errors.
-
-## What Drives the Results
-
-| Factor | Heap Impact | QPS Impact | Stability Impact |
-|--------|-------------|------------|------------------|
-| buildKey deduplication | dominant | moderate | — |
-| reduced GC pressure | secondary | primary at higher fanout | — |
-| deferred registration | leak prevention | — | critical |
-| rebinding cleanup | leak prevention | — | critical |
-| epoch guard | — | — | critical |
-
-## Conclusion
-
-The current strategy of exact-match buildKey handler reuse delivers substantial memory savings while preserving stable throughput and correctness under load.
-
-For this workload pattern, the results show:
-
-- large heap savings when many `svc_key`s collapse onto few buildKeys
-- modest but consistent QPS gains
-- stable long-running behavior under repeated flush churn
-- no need to reintroduce template sharing or SQL rewriting to get meaningful wins from handler reuse
-
-## Notes
-
-- This file is a results document for the current evaluation cycle.
-- For current script entrypoints and workflow guidance, use `README.md` in the same directory.
diff --git a/graphql/server/perf/README.md b/graphql/server/perf/README.md
index c467a520fe..951df15365 100644
--- a/graphql/server/perf/README.md
+++ b/graphql/server/perf/README.md
@@ -1,186 +1,542 @@
# Constructive GraphQL Server Perf Suite
-This directory contains the performance tooling for the GraphQL server.
+This directory is the home for GraphQL server performance tooling around the multi-tenancy cache work.
-The current optimization model being exercised is:
+This README is written for the intended TypeScript perf toolkit shape. The current branch still contains legacy `.mjs` and `.sh` scripts; until the TypeScript CLI refactor lands, use the legacy equivalents called out in each section.
+
+## What This Tests
+
+The current optimization model is exact-match buildKey handler reuse:
-- 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
-In practice, handlers are reused only when the build inputs match exactly:
+The build inputs that matter for the current cache model are:
- connection identity
- schema set
-- role configuration
+- `anonRole`
+- `roleName`
-## Perf Lanes
+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
+- target TS 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
+- target TS 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`
+
+Options such as shape variants, sweeps, stress matrices, prewarm, and longer duration runs are modifiers of the two maintained flows above.
+
+## TypeScript CLI Target
+
+The refactor should converge the current loose script set into a TypeScript CLI and library.
+
+Target command surface:
+
+| Target command | Purpose | Legacy equivalent today |
+|---|---|---|
+| `perf private-benchmark` | Run a lightweight private/header-routing HTTP benchmark | `npx ts-node graphql/server/perf/e2e-benchmark.ts` |
+| `perf private-compare` | Run old/new private-routing comparison | `bash graphql/server/perf/run-comparison.sh` |
+| `perf stress` | Run the curated private comparison stress matrix | `bash graphql/server/perf/run-stress-suite.sh` |
+| `perf public-preflight` | Provision/validate DBPM tenants and generate profiles | `node graphql/server/perf/phase1-preflight.mjs` |
+| `perf public-load` | Run DBPM-backed business load | `node graphql/server/perf/phase2-load.mjs` |
+| `perf run` | Run preflight + load for one run directory | `node graphql/server/perf/run-test-spec.mjs` |
+| `perf sweep` | Run repeated K/tenant-count sweeps | `node graphql/server/perf/run-k-sweep.mjs` |
+| `perf summarize-shapes` | Summarize DBPM shape variants | `node graphql/server/perf/summarize-shapes.mjs` |
+| `perf prepare-public-access` | Prepare public grants for perf business tables | `node graphql/server/perf/prepare-public-test-access.mjs` |
+| `perf reset-business-data` | Truncate generated business workload table data | `node graphql/server/perf/reset-business-test-data.mjs` |
+
+Target library boundaries:
+
+```text
+perf/src/cli.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/types.ts
+```
+
+The CLI should make the public/private lane split explicit. It should not keep every legacy script 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
+```
-There are two distinct lanes in this folder.
+Use `/debug/memory` to verify server readiness and inspect cache state:
-### 1. Lightweight HTTP comparison
+```bash
+curl http://localhost:3000/debug/memory
+```
-This lane drives real HTTP requests through Express -> PostGraphile -> Grafast -> PostgreSQL.
+For the new multi-tenancy cache path, useful debug fields include `multiTenancyCache.handlerCacheSize` and `graphileBuilds.started` when present.
-Primary entrypoints:
+## Perf Lanes
-- `e2e-benchmark.ts`
-- `run-comparison.sh`
-- `run-stress-suite.sh`
+| Lane | Routing | Data | Main use | Target entrypoints | Legacy entrypoints 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` | `phase1-preflight.mjs`, `phase2-load.mjs`, `run-test-spec.mjs`, `run-k-sweep.mjs` |
-Use this lane when you want:
+Do not treat every helper script as a separate product surface. Most helpers exist to support one of these two lanes.
-- old vs new mode comparison
-- QPS / latency / heap snapshots
-- repeated server restarts and cache warm/cold behavior
+## Current Server Modes
-`e2e-benchmark.ts` is the simple benchmark entrypoint. It currently exercises the private-routing path with header-based requests and a fixed query mix.
+### DBPM lane: public routing
-### 2. DBPM-backed perf framework
+Start the server in public routing mode for DBPM-backed multitenant runs:
-This lane provisions real tenant data, builds token pools and request profiles, and can be used for longer-running or more realistic tenant workflows.
+```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
+```
-Primary entrypoints:
+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.
-- `phase1-preflight.mjs`
-- `phase1-tech-validate-dbpm.mjs`
-- `phase2-load.mjs`
-- `run-test-spec.mjs`
-- `run-k-sweep.mjs`
+### Lightweight lane: private routing
-Use this lane when you want:
+Start the server in private/header-routing mode for the lightweight benchmark lane:
-- DBPM tenant provisioning
-- business-table validation
-- shape-variant experiments
-- profile-driven sustained load
+```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
+```
-## Option A Shape Variants
+### Cache mode note
-The DBPM flow now supports Option A shape divergence through additional provisioned tables created via the same provisioning mutation used for the main business table.
+`MODE=new` labels benchmark output. It does not switch an already running server.
-Relevant files:
+The server environment controls cache behavior:
-- `phase1-tech-validate-dbpm.mjs`
-- `phase1-preflight.mjs`
-- `summarize-shapes.mjs`
+- new mode: `USE_MULTI_TENANCY_CACHE=true`
+- old mode: unset `USE_MULTI_TENANCY_CACHE` and use an enlarged `GRAPHILE_CACHE_MAX` for fair comparison
-The intended usage is:
+For old-mode comparisons, enlarge `GRAPHILE_CACHE_MAX` so dedicated-instance mode is not artificially dominated by LRU churn.
-1. Provision the normal tenant database and main business table.
-2. Add extra provisioned variant tables for selected tenants with `--dbpm-shape-variants`.
-3. Keep the main CRUD workload pointed only at the original business table.
-4. Use `summarize-shapes.mjs` to inspect structural divergence without relying on raw GraphQL introspection names.
+## Quick Start: Lightweight Private Comparison
-`summarize-shapes.mjs` is a perf-local diagnostic. It is not part of the runtime handler reuse mechanism.
+Target TS command:
-## Quick Start
+```bash
+pnpm --dir graphql/server perf private-compare --k 20 --duration 300 --workers 8
+```
-### Old vs New comparison
+Legacy command today:
```bash
bash graphql/server/perf/run-comparison.sh --k 20 --duration 300 --workers 8
```
-This runs:
+This comparison runs:
-- old mode with enlarged `GRAPHILE_CACHE_MAX`
+- old mode with tuned `GRAPHILE_CACHE_MAX`
- new mode with `USE_MULTI_TENANCY_CACHE=true`
-- a simple side-by-side comparison of throughput, latency, and heap
+- side-by-side throughput, latency, and heap reporting
-### Stress suite
+For a very small single-mode smoke test, start the server in the desired cache mode and run:
+
+Target TS command:
```bash
-bash graphql/server/perf/run-stress-suite.sh
+pnpm --dir graphql/server perf private-benchmark --mode new --k 2 --duration 2 --workers 1
+```
+
+Legacy command today:
+
+```bash
+MODE=new \
+K=2 \
+DURATION=2 \
+WORKERS=1 \
+SERVER_PORT=3000 \
+NO_PROXY=localhost,127.0.0.1,::1 \
+no_proxy=localhost,127.0.0.1,::1 \
+npx ts-node graphql/server/perf/e2e-benchmark.ts
```
-This is the curated multi-run shell harness for repeated old/new comparisons under a fixed matrix of scenarios.
+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.
-### DBPM preflight + load
+## 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
+
+Target TS 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
+```
+
+Legacy command today:
+
+```bash
+RUN_DIR=/tmp/constructive-perf/dbpm-$(date +%Y%m%d-%H%M%S)
+
+NO_PROXY=localhost,127.0.0.1,::1 \
+no_proxy=localhost,127.0.0.1,::1 \
node graphql/server/perf/phase1-preflight.mjs \
- --run-dir /tmp/constructive-perf/run1 \
- --dbpm-tenant-count 20 \
- --dbpm-shape-variants 3
+ --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
+
+Target TS 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
+```
+
+Legacy command today:
+
+```bash
node graphql/server/perf/phase2-load.mjs \
- --run-dir /tmp/constructive-perf/run1 \
- --workers 8 \
- --duration-seconds 300
+ --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
```
-### Shape summary
+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.
+
+## Longer Runs
+
+### Stress suite
+
+Target TS command:
```bash
-node graphql/server/perf/summarize-shapes.mjs \
- --manifest /tmp/constructive-perf/run1/data/business-table-manifest.json
+pnpm --dir graphql/server perf stress
+```
+
+Legacy command today:
+
+```bash
+bash graphql/server/perf/run-stress-suite.sh
```
-## Key Scripts
+The stress suite is a curated multi-run private comparison harness.
-| Script | Purpose |
-|---|---|
-| `e2e-benchmark.ts` | Simple HTTP benchmark through the full GraphQL stack |
-| `run-comparison.sh` | Old vs new comparison harness |
-| `run-stress-suite.sh` | Curated multi-run stress matrix |
-| `phase1-preflight.mjs` | Preflight orchestration, DBPM validation, token/keyspace/profile setup |
-| `phase1-tech-validate-dbpm.mjs` | DBPM tenant provisioning and business-table validation |
-| `phase2-load.mjs` | Sustained profile-driven GraphQL load |
-| `run-test-spec.mjs` | Wrapper around phase orchestration |
-| `run-k-sweep.mjs` | Multi-k orchestration |
-| `build-token-pool.mjs` | Sign-in and bearer-token generation |
-| `build-keyspace-profiles.mjs` | Route/keyspace expansion |
-| `build-business-op-profiles.mjs` | Business workload profile construction |
-| `reset-business-test-data.mjs` | Cleanup between runs |
-| `prepare-public-test-access.mjs` | Public-lane preparation |
-| `public-test-access-lib.mjs` | Shared helpers for public-lane access |
-| `summarize-shapes.mjs` | Name-agnostic structural shape summary |
+### K / tenant-count sweep
-## Important Notes
+Target TS command:
-### `GRAPHILE_CACHE_MAX`
+```bash
+pnpm --dir graphql/server perf sweep \
+ --k-values 3,7 \
+ --duration-seconds 600 \
+ --workers 16
+```
-For old-mode comparisons, enlarge `GRAPHILE_CACHE_MAX` so dedicated-instance mode is not artificially dominated by LRU churn.
+Legacy command today:
-`run-comparison.sh` already does this automatically.
+```bash
+node graphql/server/perf/run-k-sweep.mjs \
+ --k-values 3,7 \
+ --duration-seconds 600 \
+ --workers 16
+```
-### Interpreting the two lanes
+Public routing should use active tenant routing. Private routing can use keyspace-style synthetic expansion.
-The lightweight benchmark lane is best for:
+## Shape Variants
-- cache-mode comparison
-- heap growth
-- restart / warmup / burst behavior
+Shape variants are an optional modifier for the DBPM public lane, not a separate primary use case.
-The DBPM-backed lane is best for:
+The DBPM flow supports Option A shape divergence through additional provisioned tables created via the same provisioning mutation used for the main business table.
-- tenant provisioning realism
-- shape-variant experiments
-- profile-driven workload behavior
+Use this when you need to check structural divergence while keeping the main CRUD workload pointed at the original business table:
-### Report file
+```bash
+pnpm --dir graphql/server perf public-preflight \
+ --run-dir "$RUN_DIR" \
+ --dbpm-tenant-count 20 \
+ --dbpm-shape-variants 3
+```
-`E2E_BENCHMARK_REPORT.md` is the archived stress-test writeup for the current multi-tenancy cache evaluation. Treat it as a results document, not as the single source of truth for script capabilities.
+Legacy command today:
+
+```bash
+node graphql/server/perf/phase1-preflight.mjs \
+ --run-dir "$RUN_DIR" \
+ --dbpm-tenant-count 20 \
+ --dbpm-shape-variants 3
+```
+
+Summarize generated shapes:
+
+Target TS command:
+
+```bash
+pnpm --dir graphql/server perf summarize-shapes \
+ --manifest "$RUN_DIR/data/business-table-manifest.json"
+```
+
+Legacy command today:
+
+```bash
+node graphql/server/perf/summarize-shapes.mjs \
+ --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 | Target command | Legacy file 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` | `phase1-preflight.mjs` | DBPM validation, tenant provisioning, token/profile setup |
+| Public lane | `perf public-load` | `phase2-load.mjs` | Sustained profile-driven GraphQL business load |
+| Wrapper | `perf run` | `run-test-spec.mjs` | Preflight + load orchestration |
+| Wrapper | `perf sweep` | `run-k-sweep.mjs` | Repeated K/tenant-count orchestration |
+| Public helper | `perf prepare-public-access` | `prepare-public-test-access.mjs` | Grant preparation for public business tables |
+| Public helper | `perf reset-business-data` | `reset-business-test-data.mjs` | Truncates business workload table data |
+| Public helper | internal library | `public-test-access-lib.mjs` | Shared public access helper logic |
+| Profile helper | internal library | `build-token-pool.mjs` | Token generation for DBPM profiles |
+| Profile helper | internal library | `build-keyspace-profiles.mjs` | Route/keyspace expansion |
+| Profile helper | internal library | `build-business-op-profiles.mjs` | Business operation profile construction |
+| Diagnostics | `perf summarize-shapes` | `summarize-shapes.mjs` | Name-agnostic structural shape summary |
## Output Layout
-Most scripts write to a run directory under `/tmp/constructive-perf/`:
+Most commands write to a run directory under `/tmp/constructive-perf/`:
```text
/
├── data/
├── logs/
+│ ├── heap/
+│ └── sampler/
├── reports/
└── tmp-scripts/
```
Typical artifacts include:
-- tenant credentials
-- token pools
+- tenant credentials and token pools
+- route/keyspace profiles
- business table manifests
- business operation profiles
- load summaries
- debug and memory snapshots
+
+## Cleanup
+
+Target TS command:
+
+```bash
+pnpm --dir graphql/server perf reset-business-data --run-dir "$RUN_DIR"
+```
+
+Legacy command today:
+
+```bash
+node graphql/server/perf/reset-business-test-data.mjs --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/REAL_MULTITENANT_E2E_CURRENT.md b/graphql/server/perf/REAL_MULTITENANT_E2E_CURRENT.md
deleted file mode 100644
index ccbdf31345..0000000000
--- a/graphql/server/perf/REAL_MULTITENANT_E2E_CURRENT.md
+++ /dev/null
@@ -1,209 +0,0 @@
-# Real Multitenant E2E Perf Runbook
-
-This is the current verified runbook for the real DBPM-backed multitenant perf
-lane in this branch. It supersedes the older local notes that assumed a single
-private route and a `postgres_perf` database.
-
-## Database
-
-Use an already deployed Constructive database. The perf scripts default to:
-
-```bash
-PGHOST=localhost
-PGPORT=5432
-PGUSER=postgres
-PGPASSWORD=password
-PGDATABASE=constructive
-```
-
-`postgres_perf` is only an optional isolated database name from older local
-notes. This branch does not include a perf-local script that creates and deploys
-that database from zero. If you want isolation, create and deploy it externally,
-then export `PGDATABASE=postgres_perf` before running these scripts.
-
-## Server Modes
-
-The current DBPM flow needs the server in public routing mode:
-
-```bash
-cd /Users/zeta/Projects/interweb/src/agents/constructive/graphql/server
-
-PGHOST=localhost \
-PGPORT=5432 \
-PGUSER=postgres \
-PGPASSWORD=password \
-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 \
-npx ts-node src/run.ts
-```
-
-Why public mode: current local DBPM provisioning exposes auth/provision/business
-through public hosts:
-
-- `auth.localhost` for `signUp` and `signIn`
-- `modules.localhost` for DBPM provisioning mutations
-- `api-dbpm-*.localhost` for provisioned business-table GraphQL
-
-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 created here do not currently create private business APIs.
-
-## Phase 1: Provision Tenants
-
-From the repo root:
-
-```bash
-cd /Users/zeta/Projects/interweb/src/agents/constructive
-
-RUN_DIR=/tmp/constructive-perf/dbpm-$(date +%Y%m%d-%H%M%S)
-
-NO_PROXY=localhost,127.0.0.1,::1 \
-no_proxy=localhost,127.0.0.1,::1 \
-node graphql/server/perf/phase1-preflight.mjs \
- --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 for a small local run:
-
-```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 the phase 2 smoke command below.
-
-## Phase 2: Business Load
-
-Run a short correctness load first:
-
-```bash
-node graphql/server/perf/phase2-load.mjs \
- --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:
-
-```json
-{
- "profileCount": 2,
- "failed": 0
-}
-```
-
-`--public-role anonymous` is required for the current public business route
-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 script only prepares schemas whose names start
-with `perf-` unless explicitly overridden.
-
-For a longer load, increase `--workers`, `--duration-seconds`, and remove
-`--disable-prewarm` after the short correctness run passes.
-
-## Lightweight HTTP Lane
-
-The lightweight benchmark is separate from DBPM and uses synthetic private
-header routing. Start the server in private mode:
-
-```bash
-cd /Users/zeta/Projects/interweb/src/agents/constructive/graphql/server
-
-PGHOST=localhost \
-PGPORT=5432 \
-PGUSER=postgres \
-PGPASSWORD=password \
-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 \
-npx ts-node src/run.ts
-```
-
-Then run:
-
-```bash
-cd /Users/zeta/Projects/interweb/src/agents/constructive
-
-MODE=new \
-K=2 \
-DURATION=2 \
-WORKERS=1 \
-SERVER_PORT=3000 \
-NO_PROXY=localhost,127.0.0.1,::1 \
-no_proxy=localhost,127.0.0.1,::1 \
-npx ts-node graphql/server/perf/e2e-benchmark.ts
-```
-
-Important: `MODE=new` labels the client-side benchmark output. It does not
-switch an already running server. The server must be started with
-`USE_MULTI_TENANCY_CACHE=true` to exercise the new cache path.
-
-For this branch's exact-match buildKey cache, multiple synthetic tenants with
-the same connection/schemas/roles/settings should map to one handler build.
-Use `/debug/memory` to confirm `multiTenancyCache.handlerCacheSize` and
-`graphileBuilds.started`.
-
-## Cleanup
-
-`reset-business-test-data.mjs` truncates business workload tables for a run. It
-does not drop DBPM-created tenant databases, API/domain rows, 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`.
-- `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.
From 30269fd8cff5812cda8ea263ad0e1e2db1bc782f Mon Sep 17 00:00:00 2001
From: Zhi Zhen
Date: Fri, 26 Jun 2026 13:27:48 +0800
Subject: [PATCH 14/17] Add TypeScript perf toolkit commands
---
graphql/server/package.json | 2 +
graphql/server/perf/README.md | 83 +++
graphql/server/perf/src/cli.ts | 64 ++
.../server/perf/src/commands/e2e-matrix.ts | 622 ++++++++++++++++++
graphql/server/perf/src/commands/legacy.ts | 38 ++
.../src/commands/prepare-public-access.ts | 101 +++
.../perf/src/commands/private-benchmark.ts | 41 ++
.../perf/src/commands/private-compare.ts | 6 +
.../server/perf/src/commands/public-load.ts | 6 +
.../perf/src/commands/public-preflight.ts | 6 +
.../perf/src/commands/reset-business-data.ts | 156 +++++
graphql/server/perf/src/commands/run.ts | 6 +
graphql/server/perf/src/commands/stress.ts | 6 +
.../perf/src/commands/summarize-shapes.ts | 169 +++++
graphql/server/perf/src/commands/sweep.ts | 6 +
graphql/server/perf/src/lib/args.ts | 77 +++
graphql/server/perf/src/lib/config.ts | 75 +++
graphql/server/perf/src/lib/graphql.ts | 4 +
graphql/server/perf/src/lib/http.ts | 63 ++
graphql/server/perf/src/lib/paths.ts | 12 +
graphql/server/perf/src/lib/pg.ts | 17 +
graphql/server/perf/src/lib/process.ts | 41 ++
graphql/server/perf/src/lib/public-access.ts | 220 +++++++
graphql/server/perf/src/lib/reports.ts | 7 +
graphql/server/perf/src/lib/run-dir.ts | 39 ++
graphql/server/perf/src/types.ts | 76 +++
graphql/server/perf/tsconfig.json | 15 +
27 files changed, 1958 insertions(+)
create mode 100644 graphql/server/perf/src/cli.ts
create mode 100644 graphql/server/perf/src/commands/e2e-matrix.ts
create mode 100644 graphql/server/perf/src/commands/legacy.ts
create mode 100644 graphql/server/perf/src/commands/prepare-public-access.ts
create mode 100644 graphql/server/perf/src/commands/private-benchmark.ts
create mode 100644 graphql/server/perf/src/commands/private-compare.ts
create mode 100644 graphql/server/perf/src/commands/public-load.ts
create mode 100644 graphql/server/perf/src/commands/public-preflight.ts
create mode 100644 graphql/server/perf/src/commands/reset-business-data.ts
create mode 100644 graphql/server/perf/src/commands/run.ts
create mode 100644 graphql/server/perf/src/commands/stress.ts
create mode 100644 graphql/server/perf/src/commands/summarize-shapes.ts
create mode 100644 graphql/server/perf/src/commands/sweep.ts
create mode 100644 graphql/server/perf/src/lib/args.ts
create mode 100644 graphql/server/perf/src/lib/config.ts
create mode 100644 graphql/server/perf/src/lib/graphql.ts
create mode 100644 graphql/server/perf/src/lib/http.ts
create mode 100644 graphql/server/perf/src/lib/paths.ts
create mode 100644 graphql/server/perf/src/lib/pg.ts
create mode 100644 graphql/server/perf/src/lib/process.ts
create mode 100644 graphql/server/perf/src/lib/public-access.ts
create mode 100644 graphql/server/perf/src/lib/reports.ts
create mode 100644 graphql/server/perf/src/lib/run-dir.ts
create mode 100644 graphql/server/perf/src/types.ts
create mode 100644 graphql/server/perf/tsconfig.json
diff --git a/graphql/server/package.json b/graphql/server/package.json
index 0da0acd1f7..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",
diff --git a/graphql/server/perf/README.md b/graphql/server/perf/README.md
index 951df15365..9b4070f731 100644
--- a/graphql/server/perf/README.md
+++ b/graphql/server/perf/README.md
@@ -57,6 +57,7 @@ These are orchestration wrappers around the two primary use cases. They are not
- `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.
@@ -75,6 +76,7 @@ Target command surface:
| `perf public-load` | Run DBPM-backed business load | `node graphql/server/perf/phase2-load.mjs` |
| `perf run` | Run preflight + load for one run directory | `node graphql/server/perf/run-test-spec.mjs` |
| `perf sweep` | Run repeated K/tenant-count sweeps | `node graphql/server/perf/run-k-sweep.mjs` |
+| `perf e2e-matrix` | Run public/private × old/new verification matrix | no direct legacy equivalent |
| `perf summarize-shapes` | Summarize DBPM shape variants | `node graphql/server/perf/summarize-shapes.mjs` |
| `perf prepare-public-access` | Prepare public grants for perf business tables | `node graphql/server/perf/prepare-public-test-access.mjs` |
| `perf reset-business-data` | Truncate generated business workload table data | `node graphql/server/perf/reset-business-test-data.mjs` |
@@ -364,6 +366,86 @@ The public access preparation should only prepare schemas whose names start with
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
@@ -457,6 +539,7 @@ node graphql/server/perf/summarize-shapes.mjs \
| Public lane | `perf public-load` | `phase2-load.mjs` | Sustained profile-driven GraphQL business load |
| Wrapper | `perf run` | `run-test-spec.mjs` | Preflight + load orchestration |
| Wrapper | `perf sweep` | `run-k-sweep.mjs` | Repeated K/tenant-count orchestration |
+| Wrapper | `perf e2e-matrix` | none | public/private × old/new verification matrix with managed-server option |
| Public helper | `perf prepare-public-access` | `prepare-public-test-access.mjs` | Grant preparation for public business tables |
| Public helper | `perf reset-business-data` | `reset-business-test-data.mjs` | Truncates business workload table data |
| Public helper | internal library | `public-test-access-lib.mjs` | Shared public access helper logic |
diff --git a/graphql/server/perf/src/cli.ts b/graphql/server/perf/src/cli.ts
new file mode 100644
index 0000000000..e3a2ff5051
--- /dev/null
+++ b/graphql/server/perf/src/cli.ts
@@ -0,0 +1,64 @@
+#!/usr/bin/env ts-node
+import { getPerfPaths } from './lib/paths';
+import { privateBenchmark } from './commands/private-benchmark';
+import { privateCompare } from './commands/private-compare';
+import { stress } from './commands/stress';
+import { publicPreflight } from './commands/public-preflight';
+import { publicLoad } from './commands/public-load';
+import { run } from './commands/run';
+import { sweep } from './commands/sweep';
+import { summarizeShapes } from './commands/summarize-shapes';
+import { preparePublicAccess } from './commands/prepare-public-access';
+import { resetBusinessData } from './commands/reset-business-data';
+import { e2eMatrix } from './commands/e2e-matrix';
+import type { CommandDefinition } from './types';
+
+const commands: 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 × old/new E2E matrix wrapper', run: e2eMatrix },
+];
+
+const commandMap = new Map(commands.map((command) => [command.name, command]));
+
+function printHelp(): void {
+ console.log(`Constructive GraphQL Server Perf CLI\n\nUsage:\n pnpm --dir graphql/server perf [options]\n\nCommands:`);
+ const max = Math.max(...commands.map((command) => command.name.length));
+ for (const command of commands) {
+ console.log(` ${command.name.padEnd(max)} ${command.summary}`);
+ }
+ console.log(`\nGlobal options:\n --dry-run Print delegated commands without executing them\n --help Show this help\n\nExamples:\n pnpm --dir graphql/server perf private-benchmark --mode new --k 2 --duration-seconds 5 --workers 1\n pnpm --dir graphql/server perf public-preflight --run-dir /tmp/constructive-perf/dbpm-smoke\n pnpm --dir graphql/server perf e2e-matrix --routing-modes private,public --cache-modes old,new --k 10 --duration-seconds 300 --workers 4 --manage-server`);
+}
+
+async function main(): Promise {
+ const [, , maybeCommand, ...rest] = process.argv;
+ if (!maybeCommand || maybeCommand === '--help' || maybeCommand === '-h' || maybeCommand === 'help') {
+ printHelp();
+ return;
+ }
+
+ const command = commandMap.get(maybeCommand);
+ if (!command) {
+ console.error(`Unknown perf command: ${maybeCommand}\n`);
+ printHelp();
+ process.exitCode = 1;
+ return;
+ }
+
+ const dryRun = rest.includes('--dry-run');
+ const args = rest.filter((arg) => arg !== '--dry-run');
+ await command.run({ args, dryRun, paths: getPerfPaths() });
+}
+
+main().catch((error) => {
+ console.error(error instanceof Error ? error.stack : String(error));
+ process.exit(1);
+});
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..6097518457
--- /dev/null
+++ b/graphql/server/perf/src/commands/e2e-matrix.ts
@@ -0,0 +1,622 @@
+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 { DEFAULT_BASE_URL, 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 child = spawn(process.execPath, [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(
+ process.execPath,
+ [
+ path.join(ctx.paths.perfDir, 'phase2-load.mjs'),
+ '--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(
+ process.execPath,
+ [
+ path.join(ctx.paths.perfDir, 'phase1-preflight.mjs'),
+ '--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', DEFAULT_BASE_URL) || DEFAULT_BASE_URL;
+ 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..92315f9b96
--- /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 runNodeLegacyScript(
+ ctx: CommandContext,
+ scriptName: string,
+ args = ctx.args,
+): Promise {
+ await runProcess(process.execPath, [path.join(ctx.paths.perfDir, scriptName), ...args], {
+ cwd: ctx.paths.repoRoot,
+ env: process.env,
+ dryRun: ctx.dryRun,
+ label: 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..514b068b41
--- /dev/null
+++ b/graphql/server/perf/src/commands/public-load.ts
@@ -0,0 +1,6 @@
+import { runNodeLegacyScript } from './legacy';
+import type { CommandContext } from '../types';
+
+export async function publicLoad(ctx: CommandContext): Promise {
+ await runNodeLegacyScript(ctx, 'phase2-load.mjs');
+}
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..fdfc83507f
--- /dev/null
+++ b/graphql/server/perf/src/commands/public-preflight.ts
@@ -0,0 +1,6 @@
+import { runNodeLegacyScript } from './legacy';
+import type { CommandContext } from '../types';
+
+export async function publicPreflight(ctx: CommandContext): Promise {
+ await runNodeLegacyScript(ctx, 'phase1-preflight.mjs');
+}
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..da08c79227
--- /dev/null
+++ b/graphql/server/perf/src/commands/run.ts
@@ -0,0 +1,6 @@
+import { runNodeLegacyScript } from './legacy';
+import type { CommandContext } from '../types';
+
+export async function run(ctx: CommandContext): Promise {
+ await runNodeLegacyScript(ctx, 'run-test-spec.mjs');
+}
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..c13dfd0add
--- /dev/null
+++ b/graphql/server/perf/src/commands/sweep.ts
@@ -0,0 +1,6 @@
+import { runNodeLegacyScript } from './legacy';
+import type { CommandContext } from '../types';
+
+export async function sweep(ctx: CommandContext): Promise {
+ await runNodeLegacyScript(ctx, 'run-k-sweep.mjs');
+}
diff --git a/graphql/server/perf/src/lib/args.ts b/graphql/server/perf/src/lib/args.ts
new file mode 100644
index 0000000000..988e33025d
--- /dev/null
+++ b/graphql/server/perf/src/lib/args.ts
@@ -0,0 +1,77 @@
+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;
+}
diff --git a/graphql/server/perf/src/lib/config.ts b/graphql/server/perf/src/lib/config.ts
new file mode 100644
index 0000000000..d1038ff9e3
--- /dev/null
+++ b/graphql/server/perf/src/lib/config.ts
@@ -0,0 +1,75 @@
+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',
+ 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/types.ts b/graphql/server/perf/src/types.ts
new file mode 100644
index 0000000000..7c4e62a817
--- /dev/null
+++ b/graphql/server/perf/src/types.ts
@@ -0,0 +1,76 @@
+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 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/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"]
+}
From 7f8814594d2e56c1f0b58f17e5fd0a2792fec992 Mon Sep 17 00:00:00 2001
From: zetazzz
Date: Mon, 29 Jun 2026 05:08:53 +0800
Subject: [PATCH 15/17] Refactor GraphQL perf CLI
---
graphql/server/perf/README.md | 182 ++---
.../perf/prepare-public-test-access.mjs | 129 ----
.../server/perf/reset-business-test-data.mjs | 187 -----
graphql/server/perf/run-k-sweep.mjs | 713 ------------------
graphql/server/perf/run-test-spec.mjs | 61 --
graphql/server/perf/seed-real-multitenant.mjs | 312 --------
graphql/server/perf/src/cli.ts | 61 +-
graphql/server/perf/src/commands.ts | 65 ++
.../server/perf/src/commands/e2e-matrix.ts | 17 +-
graphql/server/perf/src/commands/legacy.ts | 6 +-
.../server/perf/src/commands/public-load.ts | 4 +-
.../perf/src/commands/public-preflight.ts | 4 +-
graphql/server/perf/src/commands/run.ts | 26 +-
graphql/server/perf/src/commands/sweep.ts | 24 +-
.../legacy/build-business-op-profiles.ts} | 3 +-
.../legacy/build-keyspace-profiles.ts} | 3 +-
.../legacy/build-token-pool.ts} | 3 +-
.../perf/{common.mjs => src/legacy/common.ts} | 1 +
.../legacy/phase1-preflight.ts} | 24 +-
.../legacy/phase1-tech-validate-dbpm.ts} | 3 +-
.../legacy/phase2-load.ts} | 24 +-
.../legacy/public-test-access-lib.ts} | 1 +
graphql/server/perf/src/lib/args.ts | 25 +
graphql/server/perf/src/lib/config.ts | 1 +
.../perf/src/register-workspace-packages.cjs | 85 +++
graphql/server/perf/src/types.ts | 6 +
graphql/server/perf/src/utils/display.ts | 47 ++
graphql/server/perf/src/utils/index.ts | 1 +
graphql/server/perf/summarize-shapes.mjs | 222 ------
29 files changed, 381 insertions(+), 1859 deletions(-)
delete mode 100644 graphql/server/perf/prepare-public-test-access.mjs
delete mode 100644 graphql/server/perf/reset-business-test-data.mjs
delete mode 100644 graphql/server/perf/run-k-sweep.mjs
delete mode 100644 graphql/server/perf/run-test-spec.mjs
delete mode 100644 graphql/server/perf/seed-real-multitenant.mjs
create mode 100644 graphql/server/perf/src/commands.ts
rename graphql/server/perf/{build-business-op-profiles.mjs => src/legacy/build-business-op-profiles.ts} (99%)
rename graphql/server/perf/{build-keyspace-profiles.mjs => src/legacy/build-keyspace-profiles.ts} (99%)
rename graphql/server/perf/{build-token-pool.mjs => src/legacy/build-token-pool.ts} (99%)
rename graphql/server/perf/{common.mjs => src/legacy/common.ts} (99%)
rename graphql/server/perf/{phase1-preflight.mjs => src/legacy/phase1-preflight.ts} (97%)
rename graphql/server/perf/{phase1-tech-validate-dbpm.mjs => src/legacy/phase1-tech-validate-dbpm.ts} (99%)
rename graphql/server/perf/{phase2-load.mjs => src/legacy/phase2-load.ts} (98%)
rename graphql/server/perf/{public-test-access-lib.mjs => src/legacy/public-test-access-lib.ts} (99%)
create mode 100644 graphql/server/perf/src/register-workspace-packages.cjs
create mode 100644 graphql/server/perf/src/utils/display.ts
create mode 100644 graphql/server/perf/src/utils/index.ts
delete mode 100644 graphql/server/perf/summarize-shapes.mjs
diff --git a/graphql/server/perf/README.md b/graphql/server/perf/README.md
index 9b4070f731..77e51ad845 100644
--- a/graphql/server/perf/README.md
+++ b/graphql/server/perf/README.md
@@ -2,7 +2,7 @@
This directory is the home for GraphQL server performance tooling around the multi-tenancy cache work.
-This README is written for the intended TypeScript perf toolkit shape. The current branch still contains legacy `.mjs` and `.sh` scripts; until the TypeScript CLI refactor lands, use the legacy equivalents called out in each section.
+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
@@ -32,7 +32,7 @@ The maintained use cases are intentionally small.
- server env: `API_IS_PUBLIC=false`
- data setup: synthetic route profiles, no DBPM tenant provisioning
- main purpose: quick old/new cache comparison
-- target TS commands:
+- commands:
- `pnpm --dir graphql/server perf private-benchmark`
- `pnpm --dir graphql/server perf private-compare`
@@ -46,7 +46,7 @@ The maintained use cases are intentionally small.
- `modules.localhost` for DBPM provisioning mutations
- `api-dbpm-*.localhost` for provisioned business-table GraphQL
- main purpose: realistic tenant/business workload
-- target TS commands:
+- commands:
- `pnpm --dir graphql/server perf public-preflight`
- `pnpm --dir graphql/server perf public-load`
@@ -61,30 +61,31 @@ These are orchestration wrappers around the two primary use cases. They are not
Options such as shape variants, sweeps, stress matrices, prewarm, and longer duration runs are modifiers of the two maintained flows above.
-## TypeScript CLI Target
+## TypeScript CLI Surface
-The refactor should converge the current loose script set into a TypeScript CLI and library.
+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.
-Target command surface:
+Command surface:
-| Target command | Purpose | Legacy equivalent today |
+| Command | Purpose | Backing implementation today |
|---|---|---|
-| `perf private-benchmark` | Run a lightweight private/header-routing HTTP benchmark | `npx ts-node graphql/server/perf/e2e-benchmark.ts` |
-| `perf private-compare` | Run old/new private-routing comparison | `bash graphql/server/perf/run-comparison.sh` |
-| `perf stress` | Run the curated private comparison stress matrix | `bash graphql/server/perf/run-stress-suite.sh` |
-| `perf public-preflight` | Provision/validate DBPM tenants and generate profiles | `node graphql/server/perf/phase1-preflight.mjs` |
-| `perf public-load` | Run DBPM-backed business load | `node graphql/server/perf/phase2-load.mjs` |
-| `perf run` | Run preflight + load for one run directory | `node graphql/server/perf/run-test-spec.mjs` |
-| `perf sweep` | Run repeated K/tenant-count sweeps | `node graphql/server/perf/run-k-sweep.mjs` |
-| `perf e2e-matrix` | Run public/private × old/new verification matrix | no direct legacy equivalent |
-| `perf summarize-shapes` | Summarize DBPM shape variants | `node graphql/server/perf/summarize-shapes.mjs` |
-| `perf prepare-public-access` | Prepare public grants for perf business tables | `node graphql/server/perf/prepare-public-test-access.mjs` |
-| `perf reset-business-data` | Truncate generated business workload table data | `node graphql/server/perf/reset-business-test-data.mjs` |
+| `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
@@ -98,10 +99,11 @@ 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 should make the public/private lane split explicit. It should not keep every legacy script as an equally important top-level concept.
+The CLI keeps the public/private lane split explicit. It does not expose every helper as an equally important top-level concept.
## Prerequisites
@@ -138,10 +140,10 @@ For the new multi-tenancy cache path, useful debug fields include `multiTenancyC
## Perf Lanes
-| Lane | Routing | Data | Main use | Target entrypoints | Legacy entrypoints today |
+| 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` | `phase1-preflight.mjs`, `phase2-load.mjs`, `run-test-spec.mjs`, `run-k-sweep.mjs` |
+| 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.
@@ -208,18 +210,12 @@ For old-mode comparisons, enlarge `GRAPHILE_CACHE_MAX` so dedicated-instance mod
## Quick Start: Lightweight Private Comparison
-Target TS command:
+Command:
```bash
pnpm --dir graphql/server perf private-compare --k 20 --duration 300 --workers 8
```
-Legacy command today:
-
-```bash
-bash graphql/server/perf/run-comparison.sh --k 20 --duration 300 --workers 8
-```
-
This comparison runs:
- old mode with tuned `GRAPHILE_CACHE_MAX`
@@ -228,25 +224,12 @@ This comparison runs:
For a very small single-mode smoke test, start the server in the desired cache mode and run:
-Target TS command:
+Command:
```bash
pnpm --dir graphql/server perf private-benchmark --mode new --k 2 --duration 2 --workers 1
```
-Legacy command today:
-
-```bash
-MODE=new \
-K=2 \
-DURATION=2 \
-WORKERS=1 \
-SERVER_PORT=3000 \
-NO_PROXY=localhost,127.0.0.1,::1 \
-no_proxy=localhost,127.0.0.1,::1 \
-npx ts-node graphql/server/perf/e2e-benchmark.ts
-```
-
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
@@ -255,7 +238,7 @@ The public lane requires DBPM-backed setup. `API_IS_PUBLIC=true` alone is not en
### Phase 1: provision tenants and profiles
-Target TS command:
+Command:
```bash
RUN_DIR=/tmp/constructive-perf/dbpm-$(date +%Y%m%d-%H%M%S)
@@ -276,29 +259,6 @@ pnpm --dir graphql/server perf public-preflight \
--keyspace-min-route-keys 1
```
-Legacy command today:
-
-```bash
-RUN_DIR=/tmp/constructive-perf/dbpm-$(date +%Y%m%d-%H%M%S)
-
-NO_PROXY=localhost,127.0.0.1,::1 \
-no_proxy=localhost,127.0.0.1,::1 \
-node graphql/server/perf/phase1-preflight.mjs \
- --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
@@ -317,7 +277,7 @@ Warnings about `min-tenant-count`, recommended token scale, or keyspace route co
### Phase 2: short correctness load
-Target TS command:
+Command:
```bash
pnpm --dir graphql/server perf public-load \
@@ -334,23 +294,6 @@ pnpm --dir graphql/server perf public-load \
--public-role anonymous
```
-Legacy command today:
-
-```bash
-node graphql/server/perf/phase2-load.mjs \
- --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
@@ -450,23 +393,17 @@ Soft observations:
### Stress suite
-Target TS command:
+Command:
```bash
pnpm --dir graphql/server perf stress
```
-Legacy command today:
-
-```bash
-bash graphql/server/perf/run-stress-suite.sh
-```
-
The stress suite is a curated multi-run private comparison harness.
### K / tenant-count sweep
-Target TS command:
+Command:
```bash
pnpm --dir graphql/server perf sweep \
@@ -475,15 +412,6 @@ pnpm --dir graphql/server perf sweep \
--workers 16
```
-Legacy command today:
-
-```bash
-node graphql/server/perf/run-k-sweep.mjs \
- --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
@@ -501,52 +429,36 @@ pnpm --dir graphql/server perf public-preflight \
--dbpm-shape-variants 3
```
-Legacy command today:
-
-```bash
-node graphql/server/perf/phase1-preflight.mjs \
- --run-dir "$RUN_DIR" \
- --dbpm-tenant-count 20 \
- --dbpm-shape-variants 3
-```
-
Summarize generated shapes:
-Target TS command:
+Command:
```bash
pnpm --dir graphql/server perf summarize-shapes \
--manifest "$RUN_DIR/data/business-table-manifest.json"
```
-Legacy command today:
-
-```bash
-node graphql/server/perf/summarize-shapes.mjs \
- --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 | Target command | Legacy file today | Notes |
+| 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` | `phase1-preflight.mjs` | DBPM validation, tenant provisioning, token/profile setup |
-| Public lane | `perf public-load` | `phase2-load.mjs` | Sustained profile-driven GraphQL business load |
-| Wrapper | `perf run` | `run-test-spec.mjs` | Preflight + load orchestration |
-| Wrapper | `perf sweep` | `run-k-sweep.mjs` | Repeated K/tenant-count orchestration |
-| Wrapper | `perf e2e-matrix` | none | public/private × old/new verification matrix with managed-server option |
-| Public helper | `perf prepare-public-access` | `prepare-public-test-access.mjs` | Grant preparation for public business tables |
-| Public helper | `perf reset-business-data` | `reset-business-test-data.mjs` | Truncates business workload table data |
-| Public helper | internal library | `public-test-access-lib.mjs` | Shared public access helper logic |
-| Profile helper | internal library | `build-token-pool.mjs` | Token generation for DBPM profiles |
-| Profile helper | internal library | `build-keyspace-profiles.mjs` | Route/keyspace expansion |
-| Profile helper | internal library | `build-business-op-profiles.mjs` | Business operation profile construction |
-| Diagnostics | `perf summarize-shapes` | `summarize-shapes.mjs` | Name-agnostic structural shape summary |
+| 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
@@ -573,18 +485,12 @@ Typical artifacts include:
## Cleanup
-Target TS command:
+Command:
```bash
pnpm --dir graphql/server perf reset-business-data --run-dir "$RUN_DIR"
```
-Legacy command today:
-
-```bash
-node graphql/server/perf/reset-business-test-data.mjs --run-dir "$RUN_DIR"
-```
-
Cleanup semantics:
- truncates generated business workload tables for a run
diff --git a/graphql/server/perf/prepare-public-test-access.mjs b/graphql/server/perf/prepare-public-test-access.mjs
deleted file mode 100644
index 11046e605b..0000000000
--- a/graphql/server/perf/prepare-public-test-access.mjs
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/env node
-
-import fs from 'node:fs/promises';
-import path from 'node:path';
-
-import {
- DEFAULT_TMP_ROOT,
- ensureRunDirs,
- getArgValue,
- hasFlag,
- makeRunId,
- writeJson,
-} from './common.mjs';
-import {
- buildTargetsFromProfiles,
- ensurePublicAccessForTargets,
- extractProfiles,
- getUnsafeTargets,
-} from './public-test-access-lib.mjs';
-
-const args = process.argv.slice(2);
-
-const runId = getArgValue(args, '--run-id', makeRunId('graphile-cache-public-access'));
-const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
-const profilesPath = path.resolve(
- getArgValue(args, '--profiles', path.join(runDir, 'data', 'business-op-profiles.public.json')),
-);
-const dryRun = hasFlag(args, '--dry-run');
-const allowNonPerfSchema = hasFlag(args, '--allow-non-perf-schema');
-const tag = getArgValue(args, '--tag', '').trim();
-const publicRole = getArgValue(args, '--public-role', 'authenticated').trim();
-const publicReadRole = getArgValue(args, '--public-read-role', 'anonymous').trim();
-
-if (!publicRole) {
- throw new Error('--public-role cannot be empty');
-}
-
-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 readJson = async (filePath) => {
- const raw = await fs.readFile(filePath, 'utf8');
- return JSON.parse(raw);
-};
-
-const main = async () => {
- 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,
- publicRole,
- publicReadRole,
- });
-
- const report = {
- createdAt: new Date().toISOString(),
- runDir,
- profilesPath,
- options: {
- 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;
- }
-};
-
-main().catch((error) => {
- console.error(error instanceof Error ? error.stack : String(error));
- process.exit(1);
-});
diff --git a/graphql/server/perf/reset-business-test-data.mjs b/graphql/server/perf/reset-business-test-data.mjs
deleted file mode 100644
index e51bc85731..0000000000
--- a/graphql/server/perf/reset-business-test-data.mjs
+++ /dev/null
@@ -1,187 +0,0 @@
-#!/usr/bin/env node
-
-import fs from 'node:fs/promises';
-import path from 'node:path';
-import { Pool } from 'pg';
-
-import {
- DEFAULT_TMP_ROOT,
- ensureRunDirs,
- getArgValue,
- hasFlag,
- makeRunId,
- writeJson,
-} from './common.mjs';
-import {
- buildTargetsFromProfiles,
- ensurePublicAccessForTargets,
- extractProfiles,
- getUnsafeTargets,
-} from './public-test-access-lib.mjs';
-
-const args = process.argv.slice(2);
-
-const runId = getArgValue(args, '--run-id', makeRunId('graphile-cache-reset'));
-const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
-const profilesPath = path.resolve(
- getArgValue(args, '--profiles', path.join(runDir, 'data', 'business-op-profiles.json')),
-);
-const dryRun = hasFlag(args, '--dry-run');
-const allowNonPerfSchema = hasFlag(args, '--allow-non-perf-schema');
-const ensurePublicTestAccess = hasFlag(args, '--ensure-public-test-access');
-const publicRole = getArgValue(args, '--public-role', 'authenticated');
-const publicReadRole = getArgValue(args, '--public-read-role', 'anonymous');
-const tag = getArgValue(args, '--tag', '').trim();
-
-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 quoteIdent = (value) => `"${String(value).replace(/"/g, '""')}"`;
-
-const readJson = async (filePath) => {
- const raw = await fs.readFile(filePath, 'utf8');
- return JSON.parse(raw);
-};
-
-const main = async () => {
- 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 = [];
- const executed = [];
- 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 (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 = dryRun
- ? targets
- : executed.map((entry) => ({
- schemaName: entry.schemaName,
- tableName: entry.tableName,
- databaseId: entry.databaseId ?? null,
- profileKey: entry.profileKey ?? null,
- }));
- const accessResult = ensurePublicTestAccess
- ? await ensurePublicAccessForTargets({
- targets: accessTargets,
- pgConfig,
- dryRun,
- publicRole,
- publicReadRole,
- })
- : { prepared: [], failures: [] };
- const accessPrepared = accessResult.prepared;
- const accessFailures = accessResult.failures;
-
- const failures = [...truncateFailures, ...accessFailures];
-
- 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,
- allowNonPerfSchema,
- ensurePublicTestAccess,
- publicRole,
- publicReadRole: publicReadRole || null,
- tag: tag || null,
- },
- totals: {
- profileCount: profiles.length,
- targetCount: targets.length,
- truncatedCount: executed.length,
- accessPreparedCount: accessPrepared.length,
- failureCount: failures.length,
- },
- targets,
- executed,
- accessPrepared,
- truncateFailures,
- accessFailures,
- 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;
- }
-};
-
-main().catch((error) => {
- console.error(error instanceof Error ? error.stack : String(error));
- process.exit(1);
-});
diff --git a/graphql/server/perf/run-k-sweep.mjs b/graphql/server/perf/run-k-sweep.mjs
deleted file mode 100644
index 1fa054b70e..0000000000
--- a/graphql/server/perf/run-k-sweep.mjs
+++ /dev/null
@@ -1,713 +0,0 @@
-#!/usr/bin/env node
-
-import fs from 'node:fs';
-import fsp from 'node:fs/promises';
-import path from 'node:path';
-import { spawn, execFile } from 'node:child_process';
-import { fileURLToPath } from 'node:url';
-
-import {
- DEFAULT_BASE_URL,
- DEFAULT_TMP_ROOT,
- ensureRunDirs,
- getArgValue,
- hasFlag,
- makeRunId,
- postJson,
- writeJson,
-} from './common.mjs';
-
-const args = process.argv.slice(2);
-
-const runId = getArgValue(args, '--run-id', makeRunId('graphile-cache-k-sweep'));
-const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
-const baseUrl = getArgValue(args, '--base-url', DEFAULT_BASE_URL);
-const profilesPath = path.resolve(
- getArgValue(args, '--profiles', path.join(runDir, 'data', 'business-op-profiles.json')),
-);
-
-const workers = Number.parseInt(getArgValue(args, '--workers', '16'), 10);
-const durationSeconds = Number.parseInt(getArgValue(args, '--duration-seconds', '600'), 10);
-const idleSeconds = Number.parseInt(getArgValue(args, '--idle-seconds', '120'), 10);
-const minTenantCount = Number.parseInt(getArgValue(args, '--min-tenant-count', '10'), 10);
-const churnRatio = getArgValue(args, '--churn-ratio', '0.4');
-const churnWarmSeconds = getArgValue(args, '--churn-warm-seconds', '120');
-const churnCoolSeconds = getArgValue(args, '--churn-cool-seconds', '240');
-const churnCohorts = getArgValue(args, '--churn-cohorts', '2');
-const kValuesRaw = getArgValue(args, '--k-values', '3,7');
-const continueOnError = hasFlag(args, '--continue-on-error');
-const apiIsPublicRaw = getArgValue(args, '--api-is-public', 'false').trim().toLowerCase();
-const routingMode = getArgValue(
- args,
- '--routing-mode',
- apiIsPublicRaw === 'true' ? 'public' : 'private',
-)
- .trim()
- .toLowerCase();
-const tierMode = getArgValue(
- args,
- '--tier-mode',
- routingMode === 'public' ? 'active-tenants' : 'keyspace',
-)
- .trim()
- .toLowerCase();
-const keyspaceMode = getArgValue(
- args,
- '--keyspace-mode',
- tierMode === 'keyspace' ? 'auto' : 'none',
-)
- .trim()
- .toLowerCase();
-const publicRole = getArgValue(args, '--public-role', 'authenticated').trim();
-const publicReadRole = getArgValue(args, '--public-read-role', 'anonymous').trim();
-const ensurePublicTestAccess = hasFlag(args, '--ensure-public-test-access') || apiIsPublicRaw === 'true';
-
-const servicePort = Number.parseInt(getArgValue(args, '--service-port', '3000'), 10);
-const serviceOrigin = getArgValue(args, '--service-origin', '*');
-const serviceReadyTimeoutMs = Number.parseInt(getArgValue(args, '--service-ready-timeout-ms', '45000'), 10);
-const routeProbeTimeoutMs = Number.parseInt(getArgValue(args, '--route-probe-timeout-ms', '15000'), 10);
-const samplerMemoryIntervalMs = getArgValue(args, '--sampler-memory-interval-ms', '10000');
-const samplerDbIntervalMs = getArgValue(args, '--sampler-db-interval-ms', '30000');
-const graphileCacheMaxRaw = getArgValue(args, '--graphile-cache-max', process.env.GRAPHILE_CACHE_MAX || '').trim();
-const graphileSchemaWaitTimeMsRaw = getArgValue(
- args,
- '--graphile-schema-wait-time-ms',
- process.env.GRAPHILE_SCHEMA_WAIT_TIME_MS || '',
-).trim();
-const prewarmDisabled = hasFlag(args, '--disable-prewarm');
-const prewarmSampleSize = Number.parseInt(getArgValue(args, '--prewarm-sample-size', '0'), 10);
-const prewarmConcurrency = Number.parseInt(getArgValue(args, '--prewarm-concurrency', '6'), 10);
-const prewarmTimeoutMs = Number.parseInt(getArgValue(args, '--prewarm-timeout-ms', '30000'), 10);
-const prewarmMaxFailures = Number.parseInt(getArgValue(args, '--prewarm-max-failures', '0'), 10);
-const maxOldSpaceSizeMb = Math.max(
- 1024,
- Number.parseInt(getArgValue(args, '--max-old-space-size-mb', '15360'), 10) || 15360,
-);
-const nodeOptionsRaw = getArgValue(
- args,
- '--node-options',
- process.env.NODE_OPTIONS || '--heapsnapshot-signal=SIGUSR2 --expose-gc',
-);
-
-const ensureMaxOldSpaceSize = (options, sizeMb) => {
- const text = String(options || '').trim();
- const hasMaxOldSpace = /--max-old-space-size(?:=|\s+)/.test(text);
- if (hasMaxOldSpace) {
- return text;
- }
- return `--max-old-space-size=${sizeMb}${text.length > 0 ? ` ${text}` : ''}`;
-};
-
-const nodeOptions = ensureMaxOldSpaceSize(nodeOptionsRaw, maxOldSpaceSizeMb);
-const graphileCacheMax =
- graphileCacheMaxRaw.length > 0 ? Number.parseInt(graphileCacheMaxRaw, 10) : null;
-const graphileSchemaWaitTimeMs =
- graphileSchemaWaitTimeMsRaw.length > 0 ? Number.parseInt(graphileSchemaWaitTimeMsRaw, 10) : null;
-
-if (apiIsPublicRaw !== 'true' && apiIsPublicRaw !== 'false') {
- throw new Error(`Invalid --api-is-public=${apiIsPublicRaw}; expected true|false`);
-}
-const apiIsPublic = apiIsPublicRaw === 'true';
-
-const VALID_ROUTING_MODES = new Set(['private', 'public']);
-if (!VALID_ROUTING_MODES.has(routingMode)) {
- throw new Error(`Invalid --routing-mode=${routingMode}; expected private|public`);
-}
-if ((apiIsPublic && routingMode !== 'public') || (!apiIsPublic && routingMode !== 'private')) {
- throw new Error(
- `Inconsistent mode: --api-is-public=${apiIsPublicRaw} requires --routing-mode=${apiIsPublic ? 'public' : 'private'}`,
- );
-}
-
-const VALID_TIER_MODES = new Set(['keyspace', 'active-tenants']);
-if (!VALID_TIER_MODES.has(tierMode)) {
- throw new Error(`Invalid --tier-mode=${tierMode}; expected keyspace|active-tenants`);
-}
-if (routingMode === 'public' && tierMode === 'keyspace') {
- throw new Error('Public routing does not support keyspace tier mode; use --tier-mode active-tenants');
-}
-
-const VALID_KEYSPACE_MODES = new Set(['auto', 'schemata', 'none']);
-if (!VALID_KEYSPACE_MODES.has(keyspaceMode)) {
- throw new Error(`Invalid --keyspace-mode=${keyspaceMode}; expected auto|schemata|none`);
-}
-if (ensurePublicTestAccess && publicRole.length === 0) {
- throw new Error('--public-role cannot be empty when --ensure-public-test-access is enabled');
-}
-if (graphileCacheMaxRaw.length > 0 && (!Number.isFinite(graphileCacheMax) || graphileCacheMax <= 0)) {
- throw new Error(`Invalid --graphile-cache-max=${graphileCacheMaxRaw}; expected positive integer`);
-}
-if (
- graphileSchemaWaitTimeMsRaw.length > 0 &&
- (!Number.isFinite(graphileSchemaWaitTimeMs) || graphileSchemaWaitTimeMs <= 0)
-) {
- throw new Error(
- `Invalid --graphile-schema-wait-time-ms=${graphileSchemaWaitTimeMsRaw}; expected positive integer`,
- );
-}
-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 pgConfig = {
- host: getArgValue(args, '--pg-host', process.env.PGHOST || 'localhost'),
- port: getArgValue(args, '--pg-port', process.env.PGPORT || '5432'),
- 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 __filename = fileURLToPath(import.meta.url);
-const perfDir = path.dirname(__filename);
-const serverDir = path.resolve(perfDir, '..'); // graphql/server/
-const repoRoot = path.resolve(serverDir, '..', '..'); // repo root
-const phase2Script = path.join(perfDir, 'phase2-load.mjs');
-const resetScript = path.join(perfDir, 'reset-business-test-data.mjs');
-const serverRunScript = path.join(serverDir, 'src', 'run.ts');
-
-const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
-const ABSOLUTE_URL_RE = /^https?:\/\//i;
-
-const parseKValues = (raw) => {
- const values = String(raw)
- .split(',')
- .map((part) => Number.parseInt(part.trim(), 10))
- .filter((value) => Number.isFinite(value) && value > 0);
-
- if (values.length === 0) {
- throw new Error(`No valid k values from --k-values=${raw}`);
- }
-
- return [...new Set(values)];
-};
-
-const readJson = async (filePath) => {
- const raw = await fsp.readFile(filePath, 'utf8');
- return JSON.parse(raw);
-};
-
-const extractProfiles = (payload) => {
- if (Array.isArray(payload)) return payload;
- if (Array.isArray(payload?.profiles)) return payload.profiles;
- return [];
-};
-
-const isProcessAlive = (pid) => {
- try {
- process.kill(pid, 0);
- return true;
- } catch {
- return false;
- }
-};
-
-const getListeningPids = async (port) => {
- return await new Promise((resolve) => {
- execFile('lsof', ['-tiTCP:' + String(port), '-sTCP:LISTEN'], (error, stdout) => {
- if (error) {
- resolve([]);
- return;
- }
-
- const pids = String(stdout)
- .split('\n')
- .map((line) => Number.parseInt(line.trim(), 10))
- .filter((pid) => Number.isFinite(pid));
- resolve([...new Set(pids)]);
- });
- });
-};
-
-const stopPortListeners = async (port) => {
- const pids = await getListeningPids(port);
- if (pids.length === 0) return;
-
- for (const pid of pids) {
- try {
- process.kill(pid, 'SIGTERM');
- } catch {
- // ignore stale pid
- }
- }
-
- const deadline = Date.now() + 4000;
- while (Date.now() < deadline) {
- const alive = pids.filter((pid) => isProcessAlive(pid));
- if (alive.length === 0) return;
- await sleep(200);
- }
-
- for (const pid of pids) {
- if (!isProcessAlive(pid)) continue;
- try {
- process.kill(pid, 'SIGKILL');
- } catch {
- // ignore stale pid
- }
- }
-};
-
-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 runNodeScriptWithLog = async ({
- scriptPath,
- scriptArgs,
- cwd,
- env,
- logPath,
- abortOnExitProcess = null,
- abortProcessLabel = 'watched process',
-}) => {
- await fsp.mkdir(path.dirname(logPath), { recursive: true });
- const logStream = fs.createWriteStream(logPath, { flags: 'a' });
-
- return await new Promise((resolve) => {
- const child = spawn(process.execPath, [scriptPath, ...scriptArgs], {
- cwd,
- env,
- stdio: ['ignore', 'pipe', 'pipe'],
- });
-
- let stdout = '';
- let stderr = '';
- let abortedByGuard = null;
-
- const terminateChild = (reason) => {
- if (abortedByGuard || child.exitCode !== null || child.killed) {
- return;
- }
-
- abortedByGuard = reason;
- child.kill('SIGTERM');
- const timer = setTimeout(() => {
- if (child.exitCode === null && !child.killed) {
- child.kill('SIGKILL');
- }
- }, 3000);
- timer.unref?.();
- };
-
- let onAbortProcessExit = null;
- if (abortOnExitProcess) {
- onAbortProcessExit = (code, signal) => {
- terminateChild(
- `${abortProcessLabel} exited (code=${code ?? 'null'}, signal=${signal ?? 'null'})`,
- );
- };
- abortOnExitProcess.once('exit', onAbortProcessExit);
- }
-
- child.stdout.on('data', (chunk) => {
- const text = String(chunk);
- stdout += text;
- logStream.write(text);
- process.stdout.write(text);
- });
-
- child.stderr.on('data', (chunk) => {
- const text = String(chunk);
- stderr += text;
- logStream.write(text);
- process.stderr.write(text);
- });
-
- child.on('close', (code) => {
- if (abortOnExitProcess && onAbortProcessExit) {
- abortOnExitProcess.off('exit', onAbortProcessExit);
- }
- logStream.end();
- resolve({
- code: Number.isFinite(code) ? code : -1,
- stdout,
- stderr,
- abortedByGuard,
- });
- });
- });
-};
-
-const waitForServerReady = async ({ url, timeoutMs }) => {
- const deadline = Date.now() + timeoutMs;
- let lastError = null;
-
- while (Date.now() < deadline) {
- try {
- const response = await fetch(url, { method: 'GET' });
- if (response.ok) {
- return;
- }
- lastError = `status ${response.status}`;
- } catch (error) {
- lastError = error instanceof Error ? error.message : String(error);
- }
-
- await sleep(500);
- }
-
- throw new Error(`Server ready check timed out for ${url}; lastError=${lastError ?? 'n/a'}`);
-};
-
-const runRouteProbe = async ({ profile }) => {
- const result = await postJson({
- url: resolveProfileGraphqlUrl(profile),
- headers: profile.headers || {},
- payload: { query: '{ __typename }' },
- timeoutMs: routeProbeTimeoutMs,
- });
- const hasGraphQLErrors = Array.isArray(result.json?.errors) && result.json.errors.length > 0;
- const ok = result.ok && !hasGraphQLErrors && result.json?.data?.__typename === 'Query';
- if (!ok) {
- const firstError = hasGraphQLErrors ? result.json.errors[0]?.message ?? 'unknown GraphQL error' : null;
- throw new Error(
- `Route probe failed status=${result.status} profile=${profile.key ?? 'unknown'} msg=${result.error ?? firstError ?? 'unexpected GraphQL response'}`,
- );
- }
-};
-
-const stopServiceChild = async (child) => {
- if (!child) return;
- if (child.exitCode !== null || child.killed) return;
-
- child.kill('SIGINT');
-
- const closed = await new Promise((resolve) => {
- const timer = setTimeout(() => resolve(false), 8000);
- child.once('close', () => {
- clearTimeout(timer);
- resolve(true);
- });
- });
-
- if (!closed && child.exitCode === null) {
- child.kill('SIGKILL');
- }
-};
-
-const startServiceForK = async ({ k, serviceLogPath, samplerDir, firstProfile }) => {
- await fsp.mkdir(path.dirname(serviceLogPath), { recursive: true });
- await fsp.mkdir(samplerDir, { recursive: true });
- const logStream = fs.createWriteStream(serviceLogPath, { flags: 'a' });
-
- // Use npx ts-node to start the server (Constructive repo convention)
- const child = spawn(
- 'npx',
- ['ts-node', serverRunScript],
- {
- cwd: serverDir,
- env: {
- ...process.env,
- NODE_ENV: 'development',
- GRAPHILE_ENV: 'development',
- GRAPHQL_OBSERVABILITY_ENABLED: 'true',
- GRAPHQL_DEBUG_SAMPLER_ENABLED: 'true',
- GRAPHQL_DEBUG_SAMPLER_MEMORY_INTERVAL_MS: String(samplerMemoryIntervalMs),
- GRAPHQL_DEBUG_SAMPLER_DB_INTERVAL_MS: String(samplerDbIntervalMs),
- GRAPHQL_DEBUG_SAMPLER_DIR: samplerDir,
- API_IS_PUBLIC: apiIsPublic ? 'true' : 'false',
- SERVER_PORT: String(servicePort),
- SERVER_ORIGIN: String(serviceOrigin),
- PGHOST: String(pgConfig.host),
- PGPORT: String(pgConfig.port),
- PGDATABASE: String(pgConfig.database),
- PGUSER: String(pgConfig.user),
- PGPASSWORD: String(pgConfig.password),
- NODE_OPTIONS: String(nodeOptions),
- ...(graphileCacheMax != null ? { GRAPHILE_CACHE_MAX: String(graphileCacheMax) } : {}),
- ...(graphileSchemaWaitTimeMs != null
- ? { GRAPHILE_SCHEMA_WAIT_TIME_MS: String(graphileSchemaWaitTimeMs) }
- : {}),
- },
- stdio: ['ignore', 'pipe', 'pipe'],
- },
- );
-
- child.stdout.on('data', (chunk) => {
- const text = String(chunk);
- logStream.write(text);
- process.stdout.write(text);
- });
-
- child.stderr.on('data', (chunk) => {
- const text = String(chunk);
- logStream.write(text);
- process.stderr.write(text);
- });
-
- const readyUrl = `${baseUrl}/debug/memory`;
-
- try {
- await waitForServerReady({ url: readyUrl, timeoutMs: serviceReadyTimeoutMs });
- await runRouteProbe({ profile: firstProfile });
- return { child, logStream };
- } catch (error) {
- await stopServiceChild(child);
- logStream.end();
- throw new Error(`[k=${k}] failed to start usable service: ${error instanceof Error ? error.message : String(error)}`);
- }
-};
-
-const main = async () => {
- const dirs = await ensureRunDirs(runDir);
- const kValues = parseKValues(kValuesRaw);
- const profilesPayload = await readJson(profilesPath);
- const profiles = extractProfiles(profilesPayload);
- if (profiles.length === 0) {
- throw new Error(`No profiles found in ${profilesPath}`);
- }
-
- const firstProfile = profiles[0];
- const startedAt = new Date().toISOString();
- const sweepResults = [];
- const durationMinutes = Math.max(1, Math.round(durationSeconds / 60));
- let abortError = null;
-
- const buildTierLabel = (k) => {
- if (routingMode === 'private' && tierMode === 'keyspace') {
- return `business-k${k}-${durationMinutes}m-sweep`;
- }
- if (tierMode === 'active-tenants') {
- return `business-${routingMode}-t${k}-${durationMinutes}m-sweep`;
- }
- return `business-${routingMode}-k${k}-${durationMinutes}m-sweep`;
- };
-
- for (const k of kValues) {
- const tier = buildTierLabel(k);
- const serviceLogPath = path.join(dirs.logsDir, 'service', `service-k${k}.log`);
- const resetLogPath = path.join(dirs.logsDir, 'k-sweep', `reset-k${k}.log`);
- const phase2LogPath = path.join(dirs.logsDir, 'k-sweep', `phase2-k${k}.log`);
- const samplerDir = path.join(dirs.samplerDir, `k${k}`);
- const phase2ProfileLimit = tierMode === 'active-tenants' ? Math.max(1, k) : 0;
- const phase2KeyspaceSize = tierMode === 'keyspace' ? Math.max(1, k) : 1;
- const phase2MinTenantCount = Math.max(
- 1,
- tierMode === 'active-tenants' ? Math.min(minTenantCount, phase2ProfileLimit) : minTenantCount,
- );
-
- const result = {
- k,
- tier,
- routingMode,
- tierMode,
- apiIsPublic,
- startedAt: new Date().toISOString(),
- status: 'running',
- serviceLogPath,
- resetLogPath,
- phase2LogPath,
- samplerDir,
- loadPath: path.join(dirs.dataDir, `load-${tier}.json`),
- phase2ProfileLimit,
- phase2KeyspaceSize,
- phase2MinTenantCount,
- error: null,
- };
- sweepResults.push(result);
-
- let service = null;
- try {
- await stopPortListeners(servicePort);
- service = await startServiceForK({ k, serviceLogPath, samplerDir, firstProfile });
-
- const resetExecution = await runNodeScriptWithLog({
- scriptPath: resetScript,
- scriptArgs: [
- '--run-dir',
- runDir,
- '--profiles',
- profilesPath,
- '--tag',
- `k${k}`,
- ...(ensurePublicTestAccess
- ? [
- '--ensure-public-test-access',
- '--public-role',
- publicRole,
- ...(publicReadRole.length > 0 ? ['--public-read-role', publicReadRole] : []),
- ]
- : []),
- ],
- cwd: serverDir,
- env: process.env,
- logPath: resetLogPath,
- });
- if (resetExecution.code !== 0) {
- throw new Error(`[k=${k}] reset script failed with code=${resetExecution.code}`);
- }
-
- const phase2Args = [
- '--run-dir',
- runDir,
- '--profiles',
- profilesPath,
- '--workers',
- String(workers),
- '--duration-seconds',
- String(durationSeconds),
- '--idle-seconds',
- String(idleSeconds),
- '--min-tenant-count',
- String(phase2MinTenantCount),
- '--tier',
- tier,
- '--keyspace-mode',
- keyspaceMode,
- '--keyspace-size',
- String(phase2KeyspaceSize),
- '--churn-ratio',
- String(churnRatio),
- '--churn-warm-seconds',
- String(churnWarmSeconds),
- '--churn-cool-seconds',
- String(churnCoolSeconds),
- '--churn-cohorts',
- String(churnCohorts),
- '--prewarm-concurrency',
- String(prewarmConcurrency),
- '--prewarm-timeout-ms',
- String(prewarmTimeoutMs),
- '--prewarm-max-failures',
- String(prewarmMaxFailures),
- ];
- if (prewarmDisabled) {
- phase2Args.push('--disable-prewarm');
- }
- if (prewarmSampleSize > 0) {
- phase2Args.push('--prewarm-sample-size', String(prewarmSampleSize));
- }
- if (phase2ProfileLimit > 0) {
- phase2Args.push('--profile-limit', String(phase2ProfileLimit));
- }
-
- const phase2Execution = await runNodeScriptWithLog({
- scriptPath: phase2Script,
- scriptArgs: phase2Args,
- cwd: serverDir,
- env: process.env,
- logPath: phase2LogPath,
- abortOnExitProcess: service.child,
- abortProcessLabel: `service[k=${k}]`,
- });
- if (phase2Execution.code !== 0) {
- const guardHint = phase2Execution.abortedByGuard
- ? `; aborted=${phase2Execution.abortedByGuard}`
- : '';
- throw new Error(`[k=${k}] phase2 failed with code=${phase2Execution.code}${guardHint}`);
- }
-
- await fsp.access(result.loadPath);
- const loadPayload = await readJson(result.loadPath);
- result.status = 'ok';
- result.summary = {
- total: loadPayload?.load?.total ?? 0,
- failed: loadPayload?.load?.failed ?? 0,
- requestsPerSecond: loadPayload?.load?.requestsPerSecond ?? 0,
- p95: loadPayload?.load?.latencyMs?.p95 ?? null,
- };
- result.endedAt = new Date().toISOString();
- } catch (error) {
- result.status = 'failed';
- result.error = error instanceof Error ? error.message : String(error);
- result.endedAt = new Date().toISOString();
- if (!continueOnError) {
- abortError = error;
- }
- } finally {
- if (service?.child) {
- await stopServiceChild(service.child);
- }
- if (service?.logStream) {
- service.logStream.end();
- }
- await stopPortListeners(servicePort);
- }
-
- if (abortError) {
- break;
- }
- }
-
- const summary = {
- createdAt: new Date().toISOString(),
- startedAt,
- endedAt: new Date().toISOString(),
- runDir,
- profilesPath,
- baseUrl,
- options: {
- apiIsPublic,
- routingMode,
- tierMode,
- keyspaceMode,
- ensurePublicTestAccess,
- publicRole: ensurePublicTestAccess ? publicRole : null,
- publicReadRole: ensurePublicTestAccess ? publicReadRole || null : null,
- workers,
- durationSeconds,
- idleSeconds,
- minTenantCount,
- samplerMemoryIntervalMs,
- samplerDbIntervalMs,
- graphileCacheMax,
- graphileSchemaWaitTimeMs,
- prewarm: {
- enabled: !prewarmDisabled,
- sampleSize: prewarmSampleSize,
- concurrency: prewarmConcurrency,
- timeoutMs: prewarmTimeoutMs,
- maxFailures: prewarmMaxFailures,
- },
- maxOldSpaceSizeMb,
- nodeOptions,
- kValues,
- continueOnError,
- },
- results: sweepResults,
- };
-
- const summaryPath = path.join(dirs.reportsDir, 'k-sweep-summary.json');
- await writeJson(summaryPath, summary);
-
- console.log(
- JSON.stringify(
- {
- summaryPath,
- totalRuns: sweepResults.length,
- failedRuns: sweepResults.filter((row) => row.status !== 'ok').length,
- },
- null,
- 2,
- ),
- );
-
- if (abortError) {
- throw abortError;
- }
-};
-
-main().catch(async (error) => {
- try {
- await stopPortListeners(servicePort);
- } catch {
- // ignore cleanup failure
- }
- console.error(error instanceof Error ? error.stack : String(error));
- process.exit(1);
-});
diff --git a/graphql/server/perf/run-test-spec.mjs b/graphql/server/perf/run-test-spec.mjs
deleted file mode 100644
index af8a495500..0000000000
--- a/graphql/server/perf/run-test-spec.mjs
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env node
-
-import path from 'node:path';
-import { spawn } from 'node:child_process';
-
-import {
- DEFAULT_TMP_ROOT,
- getArgValue,
- makeRunId,
-} from './common.mjs';
-
-const args = process.argv.slice(2);
-const phase = getArgValue(args, '--phase', 'all');
-const runId = getArgValue(args, '--run-id', makeRunId());
-const runDir = path.resolve(getArgValue(args, '--run-dir', path.join(DEFAULT_TMP_ROOT, runId)));
-
-const forwardArgs = args.filter((arg, index) => {
- const blocked = new Set(['--phase', '--run-id', '--run-dir']);
- if (blocked.has(arg)) return false;
- const prev = index > 0 ? args[index - 1] : '';
- if (blocked.has(prev)) return false;
- return true;
-});
-
-const runNodeScript = (scriptName, extraArgs = []) =>
- new Promise((resolve, reject) => {
- const scriptPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), scriptName);
- const child = spawn(process.execPath, [scriptPath, '--run-dir', runDir, ...extraArgs, ...forwardArgs], {
- stdio: 'inherit',
- });
-
- child.on('close', (code) => {
- if (code === 0) {
- resolve();
- } else {
- reject(new Error(`${scriptName} failed with exit code ${code}`));
- }
- });
- });
-
-const main = async () => {
- if (phase === 'phase1') {
- await runNodeScript('phase1-preflight.mjs');
- return;
- }
- if (phase === 'phase2') {
- await runNodeScript('phase2-load.mjs');
- return;
- }
- if (phase !== 'all') {
- throw new Error(`Unknown --phase value: ${phase}`);
- }
-
- await runNodeScript('phase1-preflight.mjs');
- await runNodeScript('phase2-load.mjs');
-};
-
-main().catch((error) => {
- console.error(error instanceof Error ? error.stack : String(error));
- process.exit(1);
-});
diff --git a/graphql/server/perf/seed-real-multitenant.mjs b/graphql/server/perf/seed-real-multitenant.mjs
deleted file mode 100644
index 2607263c7b..0000000000
--- a/graphql/server/perf/seed-real-multitenant.mjs
+++ /dev/null
@@ -1,312 +0,0 @@
-#!/usr/bin/env node
-
-import path from 'node:path';
-
-import {
- DEFAULT_BASE_URL,
- DEFAULT_TMP_ROOT,
- ensureRunDirs,
- getArgValue,
- makeRunId,
- postJson,
- writeJson,
-} from './common.mjs';
-
-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 tenantCount = Number.parseInt(getArgValue(args, '--tenant-count', '10'), 10);
-const userPassword = getArgValue(args, '--user-password', 'Constructive!23456');
-const userPrefix = getArgValue(args, '--user-prefix', `mtlt-${Date.now()}`);
-const orgPrefix = getArgValue(args, '--org-prefix', `mtlt-org-${Date.now()}`);
-
-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 adminEmail = getArgValue(args, '--admin-email', 'admin@constructive.io');
-const adminPassword = getArgValue(args, '--admin-password', 'admin123!@Constructive');
-
-const routeHeaders = {
- Host: routeHost,
- 'X-Api-Name': privateApiName,
- 'X-Database-Id': privateDatabaseId,
-};
-
-const gql = async ({ query, variables, headers = {} }) => {
- return await postJson({
- url: `${baseUrl}/graphql`,
- headers: {
- ...routeHeaders,
- ...headers,
- },
- payload: { query, variables },
- timeoutMs: 20000,
- });
-};
-
-const signInMutation = `
-mutation SignIn($input: SignInInput!) {
- signIn(input: $input) {
- result {
- id
- userId
- accessToken
- }
- }
-}
-`;
-
-const signUpMutation = `
-mutation SignUp($input: SignUpInput!) {
- signUp(input: $input) {
- result {
- id
- userId
- accessToken
- }
- }
-}
-`;
-
-const createUserMutation = `
-mutation CreateUser($input: CreateUserInput!) {
- createUser(input: $input) {
- user {
- id
- type
- displayName
- }
- }
-}
-`;
-
-const createOrgMembershipMutation = `
-mutation CreateOrgMembership($input: CreateOrgMembershipInput!) {
- createOrgMembership(input: $input) {
- orgMembership {
- id
- actorId
- entityId
- isOwner
- isAdmin
- isActive
- isApproved
- }
- }
-}
-`;
-
-const signInAdmin = async () => {
- const res = await gql({
- query: signInMutation,
- variables: {
- input: {
- email: adminEmail,
- password: adminPassword,
- rememberMe: true,
- },
- },
- });
-
- const token = res.json?.data?.signIn?.result?.accessToken;
- const userId = res.json?.data?.signIn?.result?.userId;
- if (!res.ok || !token || !userId) {
- throw new Error(
- `Admin sign-in failed for ${adminEmail}; status=${res.status}; error=${
- res.error || res.json?.errors?.[0]?.message || 'no token returned'
- }`,
- );
- }
-
- return { token, userId };
-};
-
-const signUpOrSignInUser = async ({ email, password }) => {
- const signUpRes = await gql({
- query: signUpMutation,
- 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,
- 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(
- `User auth failed for ${email}; signUpError=${
- signUpRes.error || signUpRes.json?.errors?.[0]?.message || 'null result'
- }; signInError=${signInRes.error || signInRes.json?.errors?.[0]?.message || 'null result'}`,
- );
-};
-
-const createOrg = async ({ adminToken, displayName }) => {
- const res = await gql({
- query: createUserMutation,
- headers: { Authorization: `Bearer ${adminToken}` },
- variables: {
- input: {
- user: {
- displayName,
- type: 2,
- },
- },
- },
- });
-
- const user = res.json?.data?.createUser?.user;
- if (!res.ok || !user?.id) {
- throw new Error(
- `Create org failed (${displayName}); status=${res.status}; error=${
- res.error || res.json?.errors?.[0]?.message || 'null result'
- }`,
- );
- }
-
- return user;
-};
-
-const createMembership = async ({ adminToken, actorId, entityId }) => {
- const res = await gql({
- query: createOrgMembershipMutation,
- headers: { Authorization: `Bearer ${adminToken}` },
- variables: {
- input: {
- orgMembership: {
- actorId,
- entityId,
- isOwner: true,
- isAdmin: true,
- isActive: true,
- isApproved: true,
- },
- },
- },
- });
-
- const membership = res.json?.data?.createOrgMembership?.orgMembership;
- if (!res.ok || !membership?.id) {
- throw new Error(
- `Create org membership failed actor=${actorId} entity=${entityId}; status=${res.status}; error=${
- res.error || res.json?.errors?.[0]?.message || 'null result'
- }`,
- );
- }
-
- return membership;
-};
-
-const main = async () => {
- if (!Number.isFinite(tenantCount) || tenantCount < 1) {
- throw new Error(`Invalid --tenant-count: ${tenantCount}`);
- }
-
- const dirs = await ensureRunDirs(runDir);
- const admin = await signInAdmin();
-
- const manifest = [];
- const credentials = [];
-
- for (let i = 1; i <= tenantCount; i += 1) {
- const idx = String(i).padStart(2, '0');
- const orgDisplayName = `${orgPrefix}-${idx}`;
- const email = `${userPrefix}-${idx}@example.com`;
-
- const org = await createOrg({ adminToken: admin.token, displayName: orgDisplayName });
- const user = await signUpOrSignInUser({ email, password: userPassword });
- const membership = await createMembership({
- adminToken: admin.token,
- actorId: user.userId,
- entityId: org.id,
- });
-
- manifest.push({
- tenantKey: `org:${org.id}`,
- orgId: org.id,
- orgDisplayName,
- userId: user.userId,
- userEmail: email,
- userAuthMode: user.mode,
- membershipId: membership.id,
- });
-
- credentials.push({
- tenantKey: `org:${org.id}`,
- email,
- password: userPassword,
- host: routeHost,
- apiName: privateApiName,
- databaseId: privateDatabaseId,
- });
- }
-
- const manifestPath = path.join(dirs.dataDir, 'tenant-manifest.json');
- const credentialsPath = path.join(dirs.dataDir, 'tenant-credentials.json');
- const summaryPath = path.join(dirs.reportsDir, 'seed-summary.json');
-
- await writeJson(manifestPath, manifest);
- await writeJson(credentialsPath, credentials);
- await writeJson(summaryPath, {
- createdAt: new Date().toISOString(),
- runDir,
- tenantCount,
- route: routeHeaders,
- adminUserId: admin.userId,
- manifestPath,
- credentialsPath,
- });
-
- console.log(
- JSON.stringify(
- {
- runDir,
- tenantCount,
- manifestPath,
- credentialsPath,
- summaryPath,
- },
- null,
- 2,
- ),
- );
-};
-
-main().catch((error) => {
- console.error(error instanceof Error ? error.stack : String(error));
- process.exit(1);
-});
diff --git a/graphql/server/perf/src/cli.ts b/graphql/server/perf/src/cli.ts
index e3a2ff5051..f62f5c2ed4 100644
--- a/graphql/server/perf/src/cli.ts
+++ b/graphql/server/perf/src/cli.ts
@@ -1,64 +1,7 @@
#!/usr/bin/env ts-node
-import { getPerfPaths } from './lib/paths';
-import { privateBenchmark } from './commands/private-benchmark';
-import { privateCompare } from './commands/private-compare';
-import { stress } from './commands/stress';
-import { publicPreflight } from './commands/public-preflight';
-import { publicLoad } from './commands/public-load';
-import { run } from './commands/run';
-import { sweep } from './commands/sweep';
-import { summarizeShapes } from './commands/summarize-shapes';
-import { preparePublicAccess } from './commands/prepare-public-access';
-import { resetBusinessData } from './commands/reset-business-data';
-import { e2eMatrix } from './commands/e2e-matrix';
-import type { CommandDefinition } from './types';
+import { commands } from './commands';
-const commands: 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 × old/new E2E matrix wrapper', run: e2eMatrix },
-];
-
-const commandMap = new Map(commands.map((command) => [command.name, command]));
-
-function printHelp(): void {
- console.log(`Constructive GraphQL Server Perf CLI\n\nUsage:\n pnpm --dir graphql/server perf [options]\n\nCommands:`);
- const max = Math.max(...commands.map((command) => command.name.length));
- for (const command of commands) {
- console.log(` ${command.name.padEnd(max)} ${command.summary}`);
- }
- console.log(`\nGlobal options:\n --dry-run Print delegated commands without executing them\n --help Show this help\n\nExamples:\n pnpm --dir graphql/server perf private-benchmark --mode new --k 2 --duration-seconds 5 --workers 1\n pnpm --dir graphql/server perf public-preflight --run-dir /tmp/constructive-perf/dbpm-smoke\n pnpm --dir graphql/server perf e2e-matrix --routing-modes private,public --cache-modes old,new --k 10 --duration-seconds 300 --workers 4 --manage-server`);
-}
-
-async function main(): Promise {
- const [, , maybeCommand, ...rest] = process.argv;
- if (!maybeCommand || maybeCommand === '--help' || maybeCommand === '-h' || maybeCommand === 'help') {
- printHelp();
- return;
- }
-
- const command = commandMap.get(maybeCommand);
- if (!command) {
- console.error(`Unknown perf command: ${maybeCommand}\n`);
- printHelp();
- process.exitCode = 1;
- return;
- }
-
- const dryRun = rest.includes('--dry-run');
- const args = rest.filter((arg) => arg !== '--dry-run');
- await command.run({ args, dryRun, paths: getPerfPaths() });
-}
-
-main().catch((error) => {
+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
index 6097518457..05952f72de 100644
--- a/graphql/server/perf/src/commands/e2e-matrix.ts
+++ b/graphql/server/perf/src/commands/e2e-matrix.ts
@@ -3,7 +3,7 @@ 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 { DEFAULT_BASE_URL, serverEnv, withLocalhostNoProxy, redactEnv } from '../lib/config';
+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';
@@ -107,7 +107,8 @@ async function startManagedServer({
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 child = spawn(process.execPath, [tsNodeBin, 'src/run.ts'], {
+ 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],
@@ -248,9 +249,10 @@ async function runPublicCase({
await removeIfExists(resultPath);
}
await runProcess(
- process.execPath,
+ 'npx',
[
- path.join(ctx.paths.perfDir, 'phase2-load.mjs'),
+ 'ts-node',
+ path.join(ctx.paths.perfDir, 'src', 'legacy', 'phase2-load.ts'),
'--run-dir',
runDir,
'--base-url',
@@ -351,9 +353,10 @@ async function provisionPublicIfRequested({
return;
}
await runProcess(
- process.execPath,
+ 'npx',
[
- path.join(ctx.paths.perfDir, 'phase1-preflight.mjs'),
+ 'ts-node',
+ path.join(ctx.paths.perfDir, 'src', 'legacy', 'phase1-preflight.ts'),
'--run-dir',
runDir,
'--base-url',
@@ -513,7 +516,7 @@ export async function e2eMatrix(ctx: CommandContext): Promise {
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', DEFAULT_BASE_URL) || DEFAULT_BASE_URL;
+ 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');
diff --git a/graphql/server/perf/src/commands/legacy.ts b/graphql/server/perf/src/commands/legacy.ts
index 92315f9b96..a67e163034 100644
--- a/graphql/server/perf/src/commands/legacy.ts
+++ b/graphql/server/perf/src/commands/legacy.ts
@@ -3,16 +3,16 @@ import { mapAliases } from '../lib/args';
import { runProcess } from '../lib/process';
import type { CommandContext } from '../types';
-export async function runNodeLegacyScript(
+export async function runMigratedTsScript(
ctx: CommandContext,
scriptName: string,
args = ctx.args,
): Promise {
- await runProcess(process.execPath, [path.join(ctx.paths.perfDir, scriptName), ...args], {
+ 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: scriptName,
+ label: `src/legacy/${scriptName}`,
});
}
diff --git a/graphql/server/perf/src/commands/public-load.ts b/graphql/server/perf/src/commands/public-load.ts
index 514b068b41..21e41561eb 100644
--- a/graphql/server/perf/src/commands/public-load.ts
+++ b/graphql/server/perf/src/commands/public-load.ts
@@ -1,6 +1,6 @@
-import { runNodeLegacyScript } from './legacy';
+import { runMigratedTsScript } from './legacy';
import type { CommandContext } from '../types';
export async function publicLoad(ctx: CommandContext): Promise {
- await runNodeLegacyScript(ctx, 'phase2-load.mjs');
+ 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
index fdfc83507f..defa6fc636 100644
--- a/graphql/server/perf/src/commands/public-preflight.ts
+++ b/graphql/server/perf/src/commands/public-preflight.ts
@@ -1,6 +1,6 @@
-import { runNodeLegacyScript } from './legacy';
+import { runMigratedTsScript } from './legacy';
import type { CommandContext } from '../types';
export async function publicPreflight(ctx: CommandContext): Promise {
- await runNodeLegacyScript(ctx, 'phase1-preflight.mjs');
+ await runMigratedTsScript(ctx, 'phase1-preflight.ts');
}
diff --git a/graphql/server/perf/src/commands/run.ts b/graphql/server/perf/src/commands/run.ts
index da08c79227..1aa8203a0c 100644
--- a/graphql/server/perf/src/commands/run.ts
+++ b/graphql/server/perf/src/commands/run.ts
@@ -1,6 +1,28 @@
-import { runNodeLegacyScript } from './legacy';
+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 {
- await runNodeLegacyScript(ctx, 'run-test-spec.mjs');
+ 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/sweep.ts b/graphql/server/perf/src/commands/sweep.ts
index c13dfd0add..5309140e06 100644
--- a/graphql/server/perf/src/commands/sweep.ts
+++ b/graphql/server/perf/src/commands/sweep.ts
@@ -1,6 +1,26 @@
-import { runNodeLegacyScript } from './legacy';
+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 {
- await runNodeLegacyScript(ctx, 'run-k-sweep.mjs');
+ 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/build-business-op-profiles.mjs b/graphql/server/perf/src/legacy/build-business-op-profiles.ts
similarity index 99%
rename from graphql/server/perf/build-business-op-profiles.mjs
rename to graphql/server/perf/src/legacy/build-business-op-profiles.ts
index c8c40c98be..0e2dcb36a7 100644
--- a/graphql/server/perf/build-business-op-profiles.mjs
+++ b/graphql/server/perf/src/legacy/build-business-op-profiles.ts
@@ -1,4 +1,5 @@
#!/usr/bin/env node
+// @ts-nocheck
import fs from 'node:fs/promises';
import path from 'node:path';
@@ -11,7 +12,7 @@ import {
getArgValue,
makeRunId,
writeJson,
-} from './common.mjs';
+} from './common';
const args = process.argv.slice(2);
diff --git a/graphql/server/perf/build-keyspace-profiles.mjs b/graphql/server/perf/src/legacy/build-keyspace-profiles.ts
similarity index 99%
rename from graphql/server/perf/build-keyspace-profiles.mjs
rename to graphql/server/perf/src/legacy/build-keyspace-profiles.ts
index 528f3fd164..e81a10b06b 100644
--- a/graphql/server/perf/build-keyspace-profiles.mjs
+++ b/graphql/server/perf/src/legacy/build-keyspace-profiles.ts
@@ -1,4 +1,5 @@
#!/usr/bin/env node
+// @ts-nocheck
import fs from 'node:fs/promises';
import path from 'node:path';
@@ -11,7 +12,7 @@ import {
makeRunId,
postJson,
writeJson,
-} from './common.mjs';
+} from './common';
const args = process.argv.slice(2);
diff --git a/graphql/server/perf/build-token-pool.mjs b/graphql/server/perf/src/legacy/build-token-pool.ts
similarity index 99%
rename from graphql/server/perf/build-token-pool.mjs
rename to graphql/server/perf/src/legacy/build-token-pool.ts
index b779987d62..90eca1133c 100644
--- a/graphql/server/perf/build-token-pool.mjs
+++ b/graphql/server/perf/src/legacy/build-token-pool.ts
@@ -1,4 +1,5 @@
#!/usr/bin/env node
+// @ts-nocheck
import fs from 'node:fs/promises';
import path from 'node:path';
@@ -11,7 +12,7 @@ import {
makeRunId,
postJson,
writeJson,
-} from './common.mjs';
+} from './common';
const args = process.argv.slice(2);
diff --git a/graphql/server/perf/common.mjs b/graphql/server/perf/src/legacy/common.ts
similarity index 99%
rename from graphql/server/perf/common.mjs
rename to graphql/server/perf/src/legacy/common.ts
index b8db375523..549fe513d4 100644
--- a/graphql/server/perf/common.mjs
+++ b/graphql/server/perf/src/legacy/common.ts
@@ -1,3 +1,4 @@
+// @ts-nocheck
import fs from 'node:fs/promises';
import http from 'node:http';
import https from 'node:https';
diff --git a/graphql/server/perf/phase1-preflight.mjs b/graphql/server/perf/src/legacy/phase1-preflight.ts
similarity index 97%
rename from graphql/server/perf/phase1-preflight.mjs
rename to graphql/server/perf/src/legacy/phase1-preflight.ts
index 2f590b8cb1..a49ae0d372 100644
--- a/graphql/server/perf/phase1-preflight.mjs
+++ b/graphql/server/perf/src/legacy/phase1-preflight.ts
@@ -1,9 +1,9 @@
#!/usr/bin/env node
+// @ts-nocheck
import fs from 'node:fs/promises';
import path from 'node:path';
import { spawn } from 'node:child_process';
-import { fileURLToPath } from 'node:url';
import { Pool } from 'pg';
import {
@@ -18,7 +18,7 @@ import {
postJson,
stableStringify,
writeJson,
-} from './common.mjs';
+} from './common';
const args = process.argv.slice(2);
@@ -238,12 +238,13 @@ const buildBootstrapCredentials = ({ readyProfiles }) => {
};
const runTokenBuilder = async () => {
- const scriptPath = fileURLToPath(new URL('./build-token-pool.mjs', import.meta.url));
+ const scriptPath = path.join(__dirname, 'build-token-pool.ts');
return await new Promise((resolve) => {
const child = spawn(
- process.execPath,
+ 'npx',
[
+ 'ts-node',
scriptPath,
'--run-dir',
runDir,
@@ -279,12 +280,13 @@ const runTokenBuilder = async () => {
};
const runDbpmTechValidationScript = async () => {
- const scriptPath = fileURLToPath(new URL('./phase1-tech-validate-dbpm.mjs', import.meta.url));
+ const scriptPath = path.join(__dirname, 'phase1-tech-validate-dbpm.ts');
return await new Promise((resolve) => {
const child = spawn(
- process.execPath,
+ 'npx',
[
+ 'ts-node',
scriptPath,
'--run-dir',
runDir,
@@ -342,12 +344,13 @@ const runDbpmTechValidationScript = async () => {
};
const runBusinessProfileBuilder = async () => {
- const scriptPath = fileURLToPath(new URL('./build-business-op-profiles.mjs', import.meta.url));
+ const scriptPath = path.join(__dirname, 'build-business-op-profiles.ts');
return await new Promise((resolve) => {
const child = spawn(
- process.execPath,
+ 'npx',
[
+ 'ts-node',
scriptPath,
'--run-dir',
runDir,
@@ -393,9 +396,10 @@ const runBusinessProfileBuilder = async () => {
};
const runKeyspaceBuilder = async () => {
- const scriptPath = fileURLToPath(new URL('./build-keyspace-profiles.mjs', import.meta.url));
+ const scriptPath = path.join(__dirname, 'build-keyspace-profiles.ts');
const cmd = [
+ 'ts-node',
scriptPath,
'--run-dir',
runDir,
@@ -422,7 +426,7 @@ const runKeyspaceBuilder = async () => {
}
return await new Promise((resolve) => {
- const child = spawn(process.execPath, cmd, {
+ const child = spawn('npx', cmd, {
stdio: ['ignore', 'pipe', 'pipe'],
});
diff --git a/graphql/server/perf/phase1-tech-validate-dbpm.mjs b/graphql/server/perf/src/legacy/phase1-tech-validate-dbpm.ts
similarity index 99%
rename from graphql/server/perf/phase1-tech-validate-dbpm.mjs
rename to graphql/server/perf/src/legacy/phase1-tech-validate-dbpm.ts
index d9298248bb..b979480d92 100644
--- a/graphql/server/perf/phase1-tech-validate-dbpm.mjs
+++ b/graphql/server/perf/src/legacy/phase1-tech-validate-dbpm.ts
@@ -1,4 +1,5 @@
#!/usr/bin/env node
+// @ts-nocheck
import path from 'node:path';
import { Pool } from 'pg';
@@ -12,7 +13,7 @@ import {
postJson,
sleep,
writeJson,
-} from './common.mjs';
+} from './common';
const args = process.argv.slice(2);
diff --git a/graphql/server/perf/phase2-load.mjs b/graphql/server/perf/src/legacy/phase2-load.ts
similarity index 98%
rename from graphql/server/perf/phase2-load.mjs
rename to graphql/server/perf/src/legacy/phase2-load.ts
index b4741793a8..7e5d255407 100644
--- a/graphql/server/perf/phase2-load.mjs
+++ b/graphql/server/perf/src/legacy/phase2-load.ts
@@ -1,5 +1,7 @@
#!/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';
@@ -7,19 +9,18 @@ import { spawn } from 'node:child_process';
import {
DEFAULT_BASE_URL,
DEFAULT_TMP_ROOT,
- ensureRunDirs,
getArgValue,
getJson,
parseIntArg,
postJson,
sleep,
writeJson,
-} from './common.mjs';
+} from './common';
import {
buildTargetsFromProfiles,
ensurePublicAccessForTargets,
getUnsafeTargets,
-} from './public-test-access-lib.mjs';
+} from './public-test-access-lib';
const args = process.argv.slice(2);
@@ -30,7 +31,18 @@ const runDir = path.resolve(
path.join(DEFAULT_TMP_ROOT, getArgValue(args, '--run-id', 'graphile-cache-leak-manual-run')),
),
);
-const dirs = await ensureRunDirs(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'),
+};
+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);
@@ -1284,7 +1296,7 @@ const tryCaptureHeap = async () => {
return { attempted: false, reason: 'heap pid not provided' };
}
- const scriptPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'scripts', 'capture-heap-snapshot.mjs');
+ 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'], {
@@ -1317,7 +1329,7 @@ const analyzeSampler = async ({ windowStart, windowEnd }) => {
return { attempted: false, reason: 'skip analyze enabled' };
}
- const scriptPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'scripts', 'analyze-debug-logs.mjs');
+ 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, {
diff --git a/graphql/server/perf/public-test-access-lib.mjs b/graphql/server/perf/src/legacy/public-test-access-lib.ts
similarity index 99%
rename from graphql/server/perf/public-test-access-lib.mjs
rename to graphql/server/perf/src/legacy/public-test-access-lib.ts
index e59c1447ce..5eb4263c54 100644
--- a/graphql/server/perf/public-test-access-lib.mjs
+++ b/graphql/server/perf/src/legacy/public-test-access-lib.ts
@@ -1,3 +1,4 @@
+// @ts-nocheck
import { Pool } from 'pg';
const quoteIdent = (value) => `"${String(value).replace(/"/g, '""')}"`;
diff --git a/graphql/server/perf/src/lib/args.ts b/graphql/server/perf/src/lib/args.ts
index 988e33025d..e227b947d5 100644
--- a/graphql/server/perf/src/lib/args.ts
+++ b/graphql/server/perf/src/lib/args.ts
@@ -75,3 +75,28 @@ export function mapAliases(args: string[], aliases: Record): str
}
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
index d1038ff9e3..e88c62f43f 100644
--- a/graphql/server/perf/src/lib/config.ts
+++ b/graphql/server/perf/src/lib/config.ts
@@ -37,6 +37,7 @@ export function serverEnv({
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),
});
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
index 7c4e62a817..b3708d1781 100644
--- a/graphql/server/perf/src/types.ts
+++ b/graphql/server/perf/src/types.ts
@@ -26,6 +26,12 @@ export interface CommandDefinition {
run: (ctx: CommandContext) => Promise;
}
+export interface PerfCliOptions {
+ paths?: PerfPaths;
+ stdout?: NodeJS.WritableStream;
+ stderr?: NodeJS.WritableStream;
+}
+
export interface RunProcessOptions {
cwd: string;
env?: NodeJS.ProcessEnv;
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/summarize-shapes.mjs b/graphql/server/perf/summarize-shapes.mjs
deleted file mode 100644
index 0b521e0ec9..0000000000
--- a/graphql/server/perf/summarize-shapes.mjs
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * summarize-shapes.mjs — Perf-local structural shape diagnostic.
- *
- * Reads the business-table-manifest.json produced by phase1-tech-validate-dbpm
- * and queries information_schema.columns for each tenant's physical schema to
- * build a **name-stripped structural fingerprint**.
- *
- * The fingerprint ignores:
- * - schema names
- * - table names
- * - column names
- *
- * It retains:
- * - per-table column count
- * - per-column data_type and is_nullable (ordered by ordinal_position)
- * - number of tables per schema
- *
- * Tenants are grouped by fingerprint so that structurally identical schemas
- * (even with different tenant-specific names) collapse into one group, while
- * genuinely different shapes (e.g. extra variant tables) form separate groups.
- *
- * Usage:
- * node summarize-shapes.mjs --manifest [pg options]
- *
- * Output:
- * - number of distinct structural groups
- * - tenant count per group
- * - column layout summary per group
- */
-
-import { createHash } from 'node:crypto';
-import { readFileSync } from 'node:fs';
-import { Pool } from 'pg';
-
-import { getArgValue } from './common.mjs';
-
-const args = process.argv.slice(2);
-
-const manifestPath = getArgValue(args, '--manifest', null);
-if (!manifestPath) {
- console.error('Usage: node summarize-shapes.mjs --manifest ');
- process.exit(1);
-}
-
-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'),
-};
-
-// ---------------------------------------------------------------------------
-// Structural fingerprinting
-// ---------------------------------------------------------------------------
-
-/**
- * Build a name-stripped structural signature for one table.
- *
- * Returns an array of [data_type, is_nullable] tuples ordered by
- * ordinal_position. The table name and column names are NOT included.
- */
-const buildTableSignature = (columns) =>
- columns
- .sort((a, b) => a.ordinal_position - b.ordinal_position)
- .map((c) => `${c.data_type}:${c.is_nullable}`);
-
-/**
- * Build a name-stripped structural fingerprint for an entire schema.
- *
- * 1. Group columns by table
- * 2. Build per-table signature (column types + nullability, ordered)
- * 3. Sort tables by their signature (lexicographic on the stringified tuple list)
- * 4. Hash the sorted result → structural fingerprint
- *
- * Two schemas with identical column layouts but different table/column names
- * will produce the same fingerprint.
- */
-const buildSchemaFingerprint = (rows) => {
- // Group by table_name
- const tables = new Map();
- for (const row of rows) {
- if (!tables.has(row.table_name)) {
- tables.set(row.table_name, []);
- }
- tables.get(row.table_name).push(row);
- }
-
- // Build per-table signatures and sort tables by signature
- const tableSignatures = [];
- for (const [, columns] of tables) {
- tableSignatures.push(buildTableSignature(columns));
- }
-
- // Sort tables by their stringified signature so table order is deterministic
- tableSignatures.sort((a, b) => {
- const sa = JSON.stringify(a);
- const sb = JSON.stringify(b);
- return sa < sb ? -1 : sa > sb ? 1 : 0;
- });
-
- const canonical = JSON.stringify(tableSignatures);
- const fingerprint = createHash('sha256').update(canonical).digest('hex').slice(0, 16);
-
- return {
- fingerprint,
- tableCount: tableSignatures.length,
- tableSummaries: tableSignatures.map((sig) => ({
- columnCount: sig.length,
- columns: sig,
- })),
- };
-};
-
-// ---------------------------------------------------------------------------
-// Main
-// ---------------------------------------------------------------------------
-
-const main = async () => {
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
- if (!Array.isArray(manifest) || manifest.length === 0) {
- console.error('Manifest is empty or not an array');
- process.exit(1);
- }
-
- const pool = new Pool(pgConfig);
-
- try {
- /** @type {Map} */
- const groups = new Map();
-
- for (const entry of manifest) {
- const schema = entry.physicalSchema;
- if (!schema) {
- console.warn(`Skipping tenant ${entry.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 ${entry.tenantKey}: no columns found in schema ${schema}`);
- continue;
- }
-
- const { fingerprint, tableCount, tableSummaries } = buildSchemaFingerprint(result.rows);
-
- if (!groups.has(fingerprint)) {
- groups.set(fingerprint, {
- tenants: [],
- tableCount,
- tableSummaries,
- });
- }
- groups.get(fingerprint).tenants.push(entry.tenantKey);
- }
-
- // ---------------------------------------------------------------------------
- // Output
- // ---------------------------------------------------------------------------
-
- 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 [fp, group] of groups) {
- groupIndex += 1;
- console.log(`--- Group ${groupIndex} (fingerprint: ${fp}) ---`);
- console.log(` Tenants: ${group.tenants.length}`);
- console.log(` Tables: ${group.tableCount}`);
- for (let t = 0; t < group.tableSummaries.length; t++) {
- const ts = group.tableSummaries[t];
- console.log(` Table ${t + 1}: ${ts.columnCount} columns`);
- for (const col of ts.columns) {
- console.log(` - ${col}`);
- }
- }
- 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('');
- }
-
- // Machine-readable summary to stdout
- const summary = {
- totalTenants: manifest.length,
- distinctGroups: groups.size,
- groups: [...groups.entries()].map(([fp, g]) => ({
- fingerprint: fp,
- tenantCount: g.tenants.length,
- tableCount: g.tableCount,
- columnLayouts: g.tableSummaries.map((ts) => ({
- columnCount: ts.columnCount,
- columns: ts.columns,
- })),
- })),
- };
-
- console.log('--- JSON Summary ---');
- console.log(JSON.stringify(summary, null, 2));
- } finally {
- await pool.end();
- }
-};
-
-main().catch((error) => {
- console.error(error instanceof Error ? error.stack : String(error));
- process.exit(1);
-});
From a8a7d02a5e4753c9582398ab5f72f2236b2afe16 Mon Sep 17 00:00:00 2001
From: Zhi Zhen
Date: Mon, 29 Jun 2026 08:57:30 +0800
Subject: [PATCH 16/17] Add GraphQL server perf benchmark suite
---
.../server-test/__tests__/perf-suite.test.ts | 154 ++++
graphql/server-test/package.json | 3 +-
graphql/server-test/perf/README.md | 71 ++
graphql/server-test/perf/SPEC.md | 855 ++++++++++++++++++
graphql/server-test/perf/e2e-matrix.perf.ts | 17 +
graphql/server-test/perf/jest.config.js | 20 +
graphql/server-test/perf/reports/.gitignore | 2 +
graphql/server-test/perf/specs/.gitkeep | 1 +
graphql/server-test/perf/specs/k10-5min.json | 25 +
.../perf/specs/private-cache-compare.json | 24 +
.../server-test/perf/specs/public-smoke.json | 23 +
graphql/server-test/perf/specs/smoke.json | 23 +
graphql/server-test/perf/src/.gitkeep | 1 +
graphql/server-test/perf/src/artifacts.ts | 95 ++
graphql/server-test/perf/src/ci-guard.ts | 25 +
graphql/server-test/perf/src/config.ts | 265 ++++++
graphql/server-test/perf/src/context.ts | 152 ++++
.../server-test/perf/src/dbpm-provision.ts | 376 ++++++++
graphql/server-test/perf/src/gates.ts | 59 ++
graphql/server-test/perf/src/load.ts | 76 ++
graphql/server-test/perf/src/matrix.ts | 320 +++++++
graphql/server-test/perf/src/memory.ts | 91 ++
graphql/server-test/perf/src/operations.ts | 254 ++++++
graphql/server-test/perf/src/preflight.ts | 179 ++++
graphql/server-test/perf/src/profiles.ts | 102 +++
graphql/server-test/perf/src/reset.ts | 35 +
graphql/server-test/perf/src/setup.ts | 78 ++
graphql/server-test/perf/src/stats.ts | 60 ++
graphql/server-test/perf/src/types.ts | 361 ++++++++
29 files changed, 3746 insertions(+), 1 deletion(-)
create mode 100644 graphql/server-test/__tests__/perf-suite.test.ts
create mode 100644 graphql/server-test/perf/README.md
create mode 100644 graphql/server-test/perf/SPEC.md
create mode 100644 graphql/server-test/perf/e2e-matrix.perf.ts
create mode 100644 graphql/server-test/perf/jest.config.js
create mode 100644 graphql/server-test/perf/reports/.gitignore
create mode 100644 graphql/server-test/perf/specs/.gitkeep
create mode 100644 graphql/server-test/perf/specs/k10-5min.json
create mode 100644 graphql/server-test/perf/specs/private-cache-compare.json
create mode 100644 graphql/server-test/perf/specs/public-smoke.json
create mode 100644 graphql/server-test/perf/specs/smoke.json
create mode 100644 graphql/server-test/perf/src/.gitkeep
create mode 100644 graphql/server-test/perf/src/artifacts.ts
create mode 100644 graphql/server-test/perf/src/ci-guard.ts
create mode 100644 graphql/server-test/perf/src/config.ts
create mode 100644 graphql/server-test/perf/src/context.ts
create mode 100644 graphql/server-test/perf/src/dbpm-provision.ts
create mode 100644 graphql/server-test/perf/src/gates.ts
create mode 100644 graphql/server-test/perf/src/load.ts
create mode 100644 graphql/server-test/perf/src/matrix.ts
create mode 100644 graphql/server-test/perf/src/memory.ts
create mode 100644 graphql/server-test/perf/src/operations.ts
create mode 100644 graphql/server-test/perf/src/preflight.ts
create mode 100644 graphql/server-test/perf/src/profiles.ts
create mode 100644 graphql/server-test/perf/src/reset.ts
create mode 100644 graphql/server-test/perf/src/setup.ts
create mode 100644 graphql/server-test/perf/src/stats.ts
create mode 100644 graphql/server-test/perf/src/types.ts
diff --git a/graphql/server-test/__tests__/perf-suite.test.ts b/graphql/server-test/__tests__/perf-suite.test.ts
new file mode 100644
index 0000000000..bd01db357d
--- /dev/null
+++ b/graphql/server-test/__tests__/perf-suite.test.ts
@@ -0,0 +1,154 @@
+import fs from 'fs/promises';
+import os from 'os';
+import path from 'path';
+
+import { redactSecrets, writeJsonArtifact } from '../perf/src/artifacts';
+import { assertPerfBenchmarkAllowed, PerfGuardError } from '../perf/src/ci-guard';
+import { loadPerfConfig } from '../perf/src/config';
+import { BenchmarkContextManager } from '../perf/src/context';
+import { evaluateCaseGates } from '../perf/src/gates';
+import { expandMatrixCases } from '../perf/src/matrix';
+import { percentile, summarizeOutcomes } from '../perf/src/stats';
+import type { MatrixCase, PerfRunConfig, PublicPreflightResult, RouteProbeSummary } from '../perf/src/types';
+
+const baseEnv = (overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv => ({
+ PERF_BENCHMARK: '1',
+ ...overrides,
+});
+
+describe('perf config loading', () => {
+ it('applies precedence defaults < group < PERF_SPEC < env overrides', async () => {
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'constructive-perf-config-'));
+ const specPath = path.join(dir, 'overlay.json');
+ await fs.writeFile(
+ specPath,
+ JSON.stringify({
+ routingModes: ['public'],
+ cacheModes: ['old'],
+ scaleProfile: { name: 'spec-scale', k: 7, durationSeconds: 11, workers: 2 },
+ publicPreflight: { allowUnderProvisioned: true },
+ })
+ );
+
+ const config = loadPerfConfig(baseEnv({
+ PERF_CONFIG_GROUP: 'private-cache-compare',
+ PERF_SPEC: specPath,
+ PERF_K: '3',
+ PERF_CACHE_MODES: 'new',
+ PERF_DURATION_SECONDS: '9',
+ PERF_WORKERS: '4',
+ PERF_CONNECTION_POLICY: 'per-case',
+ PERF_CAPTURE_MEMORY: '0',
+ }));
+
+ expect(config.routingModes).toEqual(['public']);
+ expect(config.cacheModes).toEqual(['new']);
+ expect(config.scaleProfile).toMatchObject({ name: 'spec-scale', k: 3, durationSeconds: 9, workers: 4 });
+ expect(config.publicPreflight.allowUnderProvisioned).toBe(true);
+ expect(config.connectionPolicy).toBe('per-case');
+ expect(config.captureMemory).toBe(false);
+ expect(config.source.specPath).toBe(specPath);
+ });
+});
+
+describe('perf execution guard', () => {
+ it('requires explicit PERF_BENCHMARK opt-in', () => {
+ expect(() => assertPerfBenchmarkAllowed({})).toThrow(PerfGuardError);
+ expect(() => assertPerfBenchmarkAllowed({ PERF_BENCHMARK: '1' })).not.toThrow();
+ });
+
+ it('requires ALLOW_PERF_IN_CI when CI is true', () => {
+ expect(() => assertPerfBenchmarkAllowed({ PERF_BENCHMARK: '1', CI: 'true' })).toThrow(PerfGuardError);
+ expect(() => assertPerfBenchmarkAllowed({ PERF_BENCHMARK: '1', CI: 'true', ALLOW_PERF_IN_CI: '1' })).not.toThrow();
+ });
+});
+
+describe('matrix expansion and context compatibility', () => {
+ it('expands matrix in deterministic routing/cache order', () => {
+ const config = loadPerfConfig(baseEnv({
+ PERF_ROUTING_MODES: 'public,private',
+ PERF_CACHE_MODES: 'new,old',
+ PERF_K: '2',
+ }));
+
+ expect(expandMatrixCases(config).map((item) => item.caseId)).toEqual([
+ 'private__old__smoke__metadata-read',
+ 'private__new__smoke__metadata-read',
+ 'public__old__smoke__business-crud',
+ 'public__new__smoke__business-crud',
+ ]);
+ });
+
+ it('uses routing/cache in compatibility keys and honors connection policy in config', () => {
+ const config = loadPerfConfig(baseEnv({ PERF_CONNECTION_POLICY: 'per-case' }));
+ const manager = new BenchmarkContextManager(config);
+ const matrixCase = expandMatrixCases(config)[0];
+
+ expect(config.connectionPolicy).toBe('per-case');
+ expect(manager.compatibilityKeyFor(matrixCase)).toContain('routing=private');
+ expect(manager.compatibilityKeyFor(matrixCase)).toContain('cache=new');
+ });
+});
+
+describe('artifact redaction', () => {
+ it('redacts secret-like fields and auth header values before writing JSON', async () => {
+ const redacted = redactSecrets({
+ headers: { Authorization: 'Bearer super-secret-token', Cookie: 'sid=abc' },
+ nested: { password: 'abc123', message: 'Basic abcdef' },
+ });
+ expect(JSON.stringify(redacted)).not.toContain('super-secret-token');
+ expect(JSON.stringify(redacted)).not.toContain('sid=abc');
+ expect(JSON.stringify(redacted)).not.toContain('abc123');
+
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'constructive-perf-artifact-'));
+ const artifactPath = path.join(dir, 'artifact.json');
+ await writeJsonArtifact(artifactPath, { schemaVersion: 1, Authorization: 'Bearer topsecret' });
+ const written = await fs.readFile(artifactPath, 'utf8');
+ expect(written).toContain('[REDACTED]');
+ expect(written).not.toContain('topsecret');
+ });
+});
+
+describe('stats', () => {
+ it('calculates percentiles and load summaries', () => {
+ expect(percentile([10, 30, 20, 40], 0.5)).toBe(20);
+ expect(percentile([10, 30, 20, 40], 0.95)).toBe(40);
+ const report = summarizeOutcomes({
+ durationSeconds: 2,
+ workers: 1,
+ errorSampleLimit: 20,
+ outcomes: [
+ { ok: true, latencyMs: 10, operation: 'a', requestProfileId: 'r1', unexpectedGraphqlErrors: 0 },
+ { ok: false, latencyMs: 30, operation: 'a', requestProfileId: 'r1', unexpectedGraphqlErrors: 1, error: { at: new Date().toISOString(), message: 'bad' } },
+ ],
+ });
+ expect(report.totalRequests).toBe(2);
+ expect(report.failedRequests).toBe(1);
+ expect(report.unexpectedGraphqlErrors).toBe(1);
+ expect(report.qps).toBe(1);
+ expect(report.operations.a).toEqual({ total: 2, failed: 1 });
+ });
+});
+
+describe('preflight hard gate evaluation', () => {
+ it('converts preflight and route probe failures into hard gate failures', () => {
+ const routeProbe: RouteProbeSummary = {
+ ok: false,
+ attempted: 1,
+ succeeded: 0,
+ failed: 1,
+ unexpectedGraphqlErrors: 1,
+ errorSamples: [],
+ };
+ const preflight = {
+ ok: false,
+ hardGateFailures: ['DBPM provision did not complete without unexpected errors'],
+ routeProbe,
+ } as PublicPreflightResult;
+
+ const gates = evaluateCaseGates({ preflight, routeProbe, captureMemory: false });
+ expect(gates.hardGateFailures).toContain('preflight: DBPM provision did not complete without unexpected errors');
+ expect(gates.hardGateFailures).toContain('route probes failed');
+ expect(gates.hardGateFailures).toContain('route probes returned unexpected GraphQL errors');
+ });
+});
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..6f9c1a2abb
--- /dev/null
+++ b/graphql/server-test/perf/README.md
@@ -0,0 +1,71 @@
+# 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.
+
+The earlier exploratory draft is preserved in [`README.bak`](./README.bak) for history only. Do not implement from it unless a section is explicitly promoted into this README or `SPEC.md`.
+
+## Status
+
+Draft / design in progress.
+
+We are confirming the benchmark scope, file layout, and execution model before implementing the Jest perf runner.
+
+## 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/specs 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.
+
+## 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.
+
+For the first version, the default workload profiles are:
+
+- `private`: metadata/read-oriented workload
+- `public`: business CRUD-oriented workload
+
+## 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 | Lightweight 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.
+
+## 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..4917e3e29c
--- /dev/null
+++ b/graphql/server-test/perf/SPEC.md
@@ -0,0 +1,855 @@
+# 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/specs.
+- 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.
+
+## 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-oriented workload
+- `public`: business CRUD-oriented workload
+
+## 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 | Lightweight 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`.
+ - 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 tables/data suitable for create, get-by-id, update-by-id, and list-recent style operations.
+ - Prepare benchmark-owned schemas/tables 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.
+
+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;
+- 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;
+- 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[];
+}
+```
+
+## 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
+ README.bak
+ SPEC.md
+ jest.config.js
+ e2e-matrix.perf.ts
+ specs/
+ smoke.json
+ public-smoke.json
+ private-cache-compare.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 specs. 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`, or `k10-5min` |
+| `PERF_SPEC` | unset | Optional JSON spec path. When provided, it overlays the selected config group. |
+| `PERF_RUN_DIR` | generated under `/tmp/constructive-perf/` | Artifact output directory |
+| `PERF_CONNECTION_POLICY` | `reuse` | `reuse` or `per-case` |
+
+### 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 |
+
+### 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_SPEC 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/spec/overrides produced the run.
+
+### 5.6 JSON spec shape
+
+A JSON spec 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": "metadata-read",
+ "public": "business-crud"
+ },
+ "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;
+ specPath?: 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[];
+ };
+ profiles: {
+ requestProfileCount: number;
+ operationProfileCount: number;
+ routeKeyCount: number;
+ };
+ routeProbe: RouteProbeSummary;
+ hardGateFailures: string[];
+}
+```
+
+### 6.5 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.6 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`, and `k10-5min` are represented as built-in config groups or specs;
+- public cases run DBPM provision through public preflight;
+- 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.
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..98cf511c68
--- /dev/null
+++ b/graphql/server-test/perf/e2e-matrix.perf.ts
@@ -0,0 +1,17 @@
+import { assertPerfBenchmarkAllowed } 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 benchmark', () => {
+ it('runs the selected opt-in benchmark matrix', async () => {
+ assertPerfBenchmarkAllowed();
+ const summary = await runPerfMatrix(config);
+ // eslint-disable-next-line no-console
+ console.log(`perf summary: ${summary.artifacts.summaryPath}`);
+ expect(summary.pass).toBe(true);
+ });
+});
diff --git a/graphql/server-test/perf/jest.config.js b/graphql/server-test/perf/jest.config.js
new file mode 100644
index 0000000000..e06f0fc117
--- /dev/null
+++ b/graphql/server-test/perf/jest.config.js
@@ -0,0 +1,20 @@
+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'],
+ 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/specs/.gitkeep b/graphql/server-test/perf/specs/.gitkeep
new file mode 100644
index 0000000000..0079fdcbfa
--- /dev/null
+++ b/graphql/server-test/perf/specs/.gitkeep
@@ -0,0 +1 @@
+# Placeholder for optional reusable benchmark spec JSON files.
diff --git a/graphql/server-test/perf/specs/k10-5min.json b/graphql/server-test/perf/specs/k10-5min.json
new file mode 100644
index 0000000000..295d0ac10a
--- /dev/null
+++ b/graphql/server-test/perf/specs/k10-5min.json
@@ -0,0 +1,25 @@
+{
+ "name": "k10-5min",
+ "connectionPolicy": "reuse",
+ "routingModes": [
+ "private",
+ "public"
+ ],
+ "cacheModes": [
+ "old",
+ "new"
+ ],
+ "scaleProfile": {
+ "name": "k10-5min",
+ "k": 10,
+ "durationSeconds": 300,
+ "workers": 4
+ },
+ "workloadProfiles": {
+ "private": "metadata-read",
+ "public": "business-crud"
+ },
+ "publicPreflight": {
+ "allowUnderProvisioned": false
+ }
+}
diff --git a/graphql/server-test/perf/specs/private-cache-compare.json b/graphql/server-test/perf/specs/private-cache-compare.json
new file mode 100644
index 0000000000..333f56c3ce
--- /dev/null
+++ b/graphql/server-test/perf/specs/private-cache-compare.json
@@ -0,0 +1,24 @@
+{
+ "name": "private-cache-compare",
+ "connectionPolicy": "reuse",
+ "routingModes": [
+ "private"
+ ],
+ "cacheModes": [
+ "old",
+ "new"
+ ],
+ "scaleProfile": {
+ "name": "private-cache-compare",
+ "k": 3,
+ "durationSeconds": 60,
+ "workers": 2
+ },
+ "workloadProfiles": {
+ "private": "metadata-read",
+ "public": "business-crud"
+ },
+ "publicPreflight": {
+ "allowUnderProvisioned": false
+ }
+}
diff --git a/graphql/server-test/perf/specs/public-smoke.json b/graphql/server-test/perf/specs/public-smoke.json
new file mode 100644
index 0000000000..d8622781e9
--- /dev/null
+++ b/graphql/server-test/perf/specs/public-smoke.json
@@ -0,0 +1,23 @@
+{
+ "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/specs/smoke.json b/graphql/server-test/perf/specs/smoke.json
new file mode 100644
index 0000000000..beb8a3e285
--- /dev/null
+++ b/graphql/server-test/perf/specs/smoke.json
@@ -0,0 +1,23 @@
+{
+ "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/src/.gitkeep b/graphql/server-test/perf/src/.gitkeep
new file mode 100644
index 0000000000..7c0593e150
--- /dev/null
+++ b/graphql/server-test/perf/src/.gitkeep
@@ -0,0 +1 @@
+# Placeholder for future Jest perf benchmark implementation files.
diff --git a/graphql/server-test/perf/src/artifacts.ts b/graphql/server-test/perf/src/artifacts.ts
new file mode 100644
index 0000000000..b0bced493a
--- /dev/null
+++ b/graphql/server-test/perf/src/artifacts.ts
@@ -0,0 +1,95 @@
+import fs from 'fs/promises';
+import path from 'path';
+
+import { type PerfRunConfig, type RunArtifactPaths } from './types';
+
+const SECRET_KEY_PATTERN = /(secret|token|cookie|password|authorization|api[-_]?key|private[-_]?key|session)/i;
+const AUTH_VALUE_PATTERN = /\b(Bearer|Basic)\s+[A-Za-z0-9._~+\-/]+=*/gi;
+
+export const redactSecrets = (value: unknown, parentKey = ''): unknown => {
+ if (value == null) return value;
+
+ if (typeof value === 'string') {
+ if (SECRET_KEY_PATTERN.test(parentKey)) return '[REDACTED]';
+ return value.replace(AUTH_VALUE_PATTERN, '$1 [REDACTED]');
+ }
+
+ if (typeof value !== 'object') return value;
+
+ if (value instanceof Error) {
+ return {
+ name: value.name,
+ message: redactSecrets(value.message, 'message'),
+ stack: value.stack ? String(redactSecrets(value.stack, 'stack')) : undefined,
+ };
+ }
+
+ if (Array.isArray(value)) {
+ return value.map((item) => redactSecrets(item, parentKey));
+ }
+
+ const input = value as Record;
+ const output: Record = {};
+ for (const [key, child] of Object.entries(input)) {
+ output[key] = SECRET_KEY_PATTERN.test(key) ? '[REDACTED]' : redactSecrets(child, key);
+ }
+ return output;
+};
+
+export const createRunArtifacts = async (config: PerfRunConfig): Promise => {
+ const runDir = path.resolve(config.runDir);
+ const paths: RunArtifactPaths = {
+ runDir,
+ summaryPath: path.join(runDir, 'summary.json'),
+ casesDir: path.join(runDir, 'cases'),
+ preflightDir: path.join(runDir, 'preflight'),
+ memoryDir: path.join(runDir, 'memory'),
+ errorsDir: path.join(runDir, 'errors'),
+ };
+
+ await Promise.all([
+ fs.mkdir(paths.casesDir, { recursive: true }),
+ fs.mkdir(paths.preflightDir, { recursive: true }),
+ fs.mkdir(paths.memoryDir, { recursive: true }),
+ fs.mkdir(paths.errorsDir, { recursive: true }),
+ ]);
+
+ return paths;
+};
+
+export const caseReportPath = (artifacts: RunArtifactPaths, caseId: string): string =>
+ path.join(artifacts.casesDir, `${caseId}.json`);
+
+export const preflightReportPath = (artifacts: RunArtifactPaths, caseId: string): string =>
+ path.join(artifacts.preflightDir, `${caseId}.json`);
+
+export const memoryReportPath = (artifacts: RunArtifactPaths, caseId: string, phase: 'before' | 'after'): string =>
+ path.join(artifacts.memoryDir, `${caseId}-${phase}.json`);
+
+export const errorsReportPath = (artifacts: RunArtifactPaths, caseId: string): string =>
+ path.join(artifacts.errorsDir, `${caseId}.json`);
+
+export const writeJsonArtifact = async (filePath: string, value: T): Promise => {
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
+ const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
+ const redacted = redactSecrets(value);
+ await fs.writeFile(tmp, `${JSON.stringify(redacted, null, 2)}\n`, 'utf8');
+ await fs.rename(tmp, filePath);
+ return filePath;
+};
+
+export const toRedactedErrorSample = (
+ error: unknown,
+ input: { operation?: string; requestProfileId?: string; status?: number; code?: string } = {}
+) => {
+ const message = error instanceof Error ? error.message : typeof error === 'string' ? error : JSON.stringify(redactSecrets(error));
+ return {
+ at: new Date().toISOString(),
+ operation: input.operation,
+ requestProfileId: input.requestProfileId,
+ status: input.status,
+ code: input.code,
+ message: String(redactSecrets(message)),
+ details: error instanceof Error ? redactSecrets({ name: error.name, stack: error.stack }) : redactSecrets(error),
+ };
+};
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..209b58ae71
--- /dev/null
+++ b/graphql/server-test/perf/src/ci-guard.ts
@@ -0,0 +1,25 @@
+export class PerfGuardError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'PerfGuardError';
+ }
+}
+
+const isCi = (value: string | undefined): boolean => {
+ if (!value) return false;
+ return !['0', 'false', 'no', 'off'].includes(value.trim().toLowerCase());
+};
+
+export const assertPerfBenchmarkAllowed = (env: NodeJS.ProcessEnv = process.env): void => {
+ if (env.PERF_BENCHMARK !== '1') {
+ throw new PerfGuardError(
+ 'Refusing to run graphql/server-test perf benchmarks: set PERF_BENCHMARK=1 to opt in.'
+ );
+ }
+
+ if (isCi(env.CI) && env.ALLOW_PERF_IN_CI !== '1') {
+ throw new PerfGuardError(
+ 'Refusing to run graphql/server-test perf benchmarks in CI: set ALLOW_PERF_IN_CI=1 for an explicit CI perf 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..c6749db124
--- /dev/null
+++ b/graphql/server-test/perf/src/config.ts
@@ -0,0 +1,265 @@
+import fs from 'fs';
+import path from 'path';
+
+import {
+ PERF_SCHEMA_VERSION,
+ 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 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,
+ },
+ 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: 3, durationSeconds: 60, workers: 2 },
+ routeProbeSampleSize: 0,
+ testTimeoutMs: 240_000,
+ },
+ 'k10-5min': {
+ name: 'k10-5min',
+ routingModes: ['private', 'public'],
+ cacheModes: ['old', 'new'],
+ scaleProfile: { name: 'k10-5min', k: 10, durationSeconds: 300, workers: 4 },
+ routeProbeSampleSize: 0,
+ testTimeoutMs: 900_000,
+ publicPreflight: { allowUnderProvisioned: false },
+ },
+};
+
+const resolveSpecPath = (specPath: string): string =>
+ path.isAbsolute(specPath) ? specPath : path.resolve(process.cwd(), specPath);
+
+const readSpecOverlay = (specPath?: string): PerfRunConfigOverlay | undefined => {
+ if (!specPath) return undefined;
+ const resolved = resolveSpecPath(specPath);
+ const parsed = JSON.parse(fs.readFileSync(resolved, 'utf8')) as PerfRunConfigOverlay;
+ return { ...parsed, specPath: resolved };
+};
+
+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 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;
+
+ return overlay;
+};
+
+const collectEnvOverrideStrings = (env: NodeJS.ProcessEnv): Record => {
+ const result: Record = {};
+ for (const [key, value] of Object.entries(env)) {
+ if (key.startsWith('PERF_') && value != null) result[key] = value;
+ }
+ return result;
+};
+
+const sanitizePrefix = (value: string): string => value.replace(/[^a-zA-Z0-9_]/g, '_').slice(0, 48);
+
+export const loadPerfConfig = (env: NodeJS.ProcessEnv = process.env): 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 specPath = env.PERF_SPEC ? resolveSpecPath(env.PERF_SPEC) : undefined;
+ const specOverlay = readSpecOverlay(specPath);
+ const envOverlay = envOverrides(env);
+
+ const merged = deepMerge(
+ deepMerge(deepMerge(defaults as Record, { ...groupOverlay, configGroup }), specOverlay as Record | undefined),
+ envOverlay as Record
+ ) as PerfRunConfigOverlay;
+
+ const scaleProfile = merged.scaleProfile as ScaleProfile;
+ const runDir = merged.runDir || path.join(DEFAULT_RUN_ROOT, `${configGroup}-${runId}`);
+
+ return {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId,
+ name: merged.name || configGroup,
+ configGroup,
+ specPath,
+ 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,
+ },
+ failFast: merged.failFast ?? true,
+ captureMemory: merged.captureMemory ?? true,
+ routeProbeSampleSize: merged.routeProbeSampleSize ?? 1,
+ errorSampleLimit: merged.errorSampleLimit ?? 20,
+ testTimeoutMs: merged.testTimeoutMs ?? 120_000,
+ benchmarkOwnedPrefix: sanitizePrefix(`perf_${runId}`),
+ source: {
+ defaults: 'built-in defaults',
+ group: configGroup,
+ specPath,
+ 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..c4c8037545
--- /dev/null
+++ b/graphql/server-test/perf/src/context.ts
@@ -0,0 +1,152 @@
+import path from 'path';
+
+import type { GetConnectionsResult } from '../../src/types';
+
+import { DEFAULT_DATABASE_ID, META_SCHEMAS, SIMPLE_PETS_SCHEMAS } from './operations';
+import type { BenchmarkContext, MatrixCase, PerfRunConfig } from './types';
+
+const seedRoot = path.join(__dirname, '..', '..', '..', '..', '__fixtures__', 'seed');
+const shared = (...segments: string[]) => path.join(seedRoot, ...segments);
+
+const servicesSeedAdapters = () => {
+ const { seed } = require('../../src') as typeof import('../../src');
+ return [
+ seed.sqlfile([
+ shared('services', 'setup.sql'),
+ shared('app-schemas', 'simple-pets', 'schema.sql'),
+ shared('services', 'test-data.sql'),
+ shared('app-schemas', 'simple-pets', 'test-data.sql'),
+ ]),
+ ];
+};
+
+interface ManagedContext {
+ context: BenchmarkContext;
+ inUse: boolean;
+}
+
+export class BenchmarkContextManager {
+ private readonly contexts = new Map();
+ private nextId = 1;
+ private envPatched = false;
+ private previousNodeEnv: string | undefined;
+ private previousObservability: string | undefined;
+
+ constructor(private readonly config: PerfRunConfig) {}
+
+ compatibilityKeyFor(matrixCase: MatrixCase): string {
+ return [
+ `routing=${matrixCase.routingMode}`,
+ `cache=${matrixCase.cacheMode}`,
+ 'seed=simple-pets-services',
+ `memory=${this.config.captureMemory ? 'capture' : 'skip'}`,
+ ].join('|');
+ }
+
+ async acquire(matrixCase: MatrixCase): Promise {
+ const compatibilityKey = this.config.connectionPolicy === 'per-case'
+ ? `${this.compatibilityKeyFor(matrixCase)}|case=${matrixCase.caseId}|seq=${this.nextId}`
+ : this.compatibilityKeyFor(matrixCase);
+
+ 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 };
+ }
+
+ // Server-level settings such as public/private routing and old/new cache mode
+ // are process-global enough that keeping incompatible contexts alive together
+ // can leak service/cache entries across cases. Reuse compatible contexts, but
+ // tear down incompatible ones before starting a fresh getConnections() server.
+ 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.restoreObservabilityEnv();
+ }
+
+ private patchObservabilityEnv(): void {
+ if (!this.config.captureMemory || this.envPatched) return;
+ this.envPatched = true;
+ this.previousNodeEnv = process.env.NODE_ENV;
+ this.previousObservability = process.env.GRAPHQL_OBSERVABILITY_ENABLED;
+
+ if (process.env.NODE_ENV !== 'development') {
+ process.env.NODE_ENV = 'development';
+ }
+ if (!process.env.GRAPHQL_OBSERVABILITY_ENABLED) {
+ process.env.GRAPHQL_OBSERVABILITY_ENABLED = 'true';
+ }
+ }
+
+ private restoreObservabilityEnv(): void {
+ if (!this.envPatched) return;
+ if (this.previousNodeEnv === undefined) delete process.env.NODE_ENV;
+ else process.env.NODE_ENV = this.previousNodeEnv;
+
+ if (this.previousObservability === undefined) delete process.env.GRAPHQL_OBSERVABILITY_ENABLED;
+ else process.env.GRAPHQL_OBSERVABILITY_ENABLED = this.previousObservability;
+ this.envPatched = false;
+ }
+
+ private async createContext(matrixCase: MatrixCase, compatibilityKey: string): Promise {
+ this.patchObservabilityEnv();
+
+ const { getConnections } = require('../../src') as typeof import('../../src');
+ const conn: GetConnectionsResult = await getConnections(
+ {
+ schemas: SIMPLE_PETS_SCHEMAS,
+ authRole: 'anonymous',
+ server: {
+ api: {
+ enableServicesApi: true,
+ isPublic: matrixCase.routingMode === 'public',
+ metaSchemas: META_SCHEMAS,
+ defaultDatabaseId: DEFAULT_DATABASE_ID,
+ useMultiTenancyCache: matrixCase.cacheMode === 'new',
+ } as any,
+ },
+ },
+ servicesSeedAdapters()
+ );
+
+ const id = `ctx-${this.nextId++}`;
+ return {
+ id,
+ compatibilityKey,
+ reused: false,
+ createdAt: new Date().toISOString(),
+ serverUrl: conn.server.url,
+ graphqlUrl: conn.server.graphqlUrl,
+ conn,
+ };
+ }
+
+ private async teardownContext(key: string): Promise {
+ const managed = this.contexts.get(key);
+ if (!managed) return;
+ this.contexts.delete(key);
+ 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..874c7a1f6b
--- /dev/null
+++ b/graphql/server-test/perf/src/dbpm-provision.ts
@@ -0,0 +1,376 @@
+import { toRedactedErrorSample } from './artifacts';
+import {
+ DEFAULT_DATABASE_ID,
+ executeGraphql,
+ PUBLIC_HOST,
+ publicHostHeaders,
+ responseErrorSample,
+ responseHasUnexpectedErrors,
+} from './operations';
+import { buildPublicOperationProfiles } from './operations';
+import type {
+ BenchmarkContext,
+ DbpmProvisionResult,
+ MatrixCase,
+ PerfRunConfig,
+ RedactedErrorSample,
+ RequestProfile,
+} from './types';
+
+interface ApiNode {
+ id: string;
+ databaseId: string;
+ name: string;
+ dbname?: string;
+ isPublic: boolean;
+ roleName?: string;
+ anonRole?: string;
+}
+
+interface DomainNode {
+ id: string;
+ databaseId: string;
+ apiId?: string;
+ subdomain?: string | null;
+ domain?: string | null;
+}
+
+interface ApiSchemaNode {
+ id: string;
+ databaseId: string;
+ apiId: string;
+ schemaId: string;
+}
+
+interface ServicesSnapshot {
+ apis?: { nodes: ApiNode[] };
+ domains?: { nodes: DomainNode[] };
+ apiSchemas?: { nodes: ApiSchemaNode[] };
+}
+
+const servicesSnapshotQuery = `query PerfServicesSnapshot($first: Int!) {
+ apis(first: $first) { nodes { id databaseId name dbname isPublic roleName anonRole } }
+ domains(first: $first) { nodes { id databaseId apiId subdomain domain } }
+ apiSchemas(first: $first) { nodes { id databaseId apiId schemaId } }
+}`;
+
+const createApiMutation = `mutation PerfCreateApi($input: CreateApiInput!) {
+ createApi(input: $input) { api { id databaseId name dbname isPublic roleName anonRole } }
+}`;
+
+const createDomainMutation = `mutation PerfCreateDomain($input: CreateDomainInput!) {
+ createDomain(input: $input) { domain { id databaseId apiId subdomain domain } }
+}`;
+
+const createApiSchemaMutation = `mutation PerfCreateApiSchema($input: CreateApiSchemaInput!) {
+ createApiSchema(input: $input) { apiSchema { id databaseId apiId schemaId } }
+}`;
+
+const hostFromDomain = (domain: DomainNode): string | null => {
+ if (!domain.domain) return null;
+ return domain.subdomain ? `${domain.subdomain}.${domain.domain}` : domain.domain;
+};
+
+const profileFromDomain = (domain: DomainNode, index: number, benchmarkOwned: boolean): RequestProfile | null => {
+ const host = hostFromDomain(domain);
+ if (!host) return null;
+ return {
+ id: benchmarkOwned ? `public-benchmark-${index}` : 'public-app',
+ routingMode: 'public',
+ routeKey: host,
+ headers: publicHostHeaders(host),
+ description: benchmarkOwned
+ ? 'Benchmark-owned public host route provisioned for perf.'
+ : 'Pre-seeded public app host route from simple-seed-services.',
+ benchmarkOwned,
+ metadata: {
+ host,
+ domainId: domain.id,
+ apiId: domain.apiId,
+ databaseId: domain.databaseId,
+ source: benchmarkOwned ? 'graphql-provision' : 'simple-seed-services',
+ },
+ };
+};
+
+const publicProfilesFromSnapshot = (
+ snapshot: ServicesSnapshot,
+ config: PerfRunConfig,
+ matrixCase: MatrixCase
+): RequestProfile[] => {
+ const domains = snapshot.domains?.nodes || [];
+ const publicApis = new Map((snapshot.apis?.nodes || []).filter((api) => api.isPublic).map((api) => [api.id, api]));
+ const casePrefix = `${config.benchmarkOwnedPrefix}_${matrixCase.caseId}`.replace(/_/g, '-').toLowerCase();
+
+ const benchmarkDomains = domains.filter((domain) => {
+ const api = domain.apiId ? publicApis.get(domain.apiId) : undefined;
+ const host = hostFromDomain(domain) || '';
+ return Boolean(api && host.includes(casePrefix));
+ });
+
+ const defaultApp = domains.find((domain) => {
+ const api = domain.apiId ? publicApis.get(domain.apiId) : undefined;
+ return api?.name === 'app' && hostFromDomain(domain) === PUBLIC_HOST;
+ });
+
+ const profiles = benchmarkDomains
+ .map((domain, index) => profileFromDomain(domain, index + 1, true))
+ .filter(Boolean) as RequestProfile[];
+
+ if (defaultApp) {
+ profiles.unshift(profileFromDomain(defaultApp, 0, false)!);
+ }
+
+ return profiles;
+};
+
+const querySnapshotViaGraphql = async (
+ context: BenchmarkContext,
+ headers: Record
+): Promise<{ snapshot?: ServicesSnapshot; errors: RedactedErrorSample[]; unavailable: boolean }> => {
+ const response = await executeGraphql(context, {
+ query: servicesSnapshotQuery,
+ variables: { first: 200 },
+ headers,
+ });
+
+ if (responseHasUnexpectedErrors(response)) {
+ return {
+ errors: [responseErrorSample(response, { operation: 'dbpm.snapshot' })],
+ unavailable: true,
+ };
+ }
+
+ return { snapshot: response.body.data, errors: [], unavailable: false };
+};
+
+const tryGraphqlProvision = async (input: {
+ context: BenchmarkContext;
+ matrixCase: MatrixCase;
+ config: PerfRunConfig;
+ snapshot: ServicesSnapshot;
+ needed: number;
+}): Promise<{ createdIds: string[]; errors: RedactedErrorSample[] }> => {
+ const { context, matrixCase, config, snapshot, needed } = input;
+ const errors: RedactedErrorSample[] = [];
+ const createdIds: string[] = [];
+
+ const appApi = (snapshot.apis?.nodes || []).find((api) => api.name === 'app' && api.isPublic);
+ const appSchemas = (snapshot.apiSchemas?.nodes || []).filter((apiSchema) => apiSchema.apiId === appApi?.id);
+ if (!appApi || appSchemas.length === 0) {
+ return {
+ createdIds,
+ errors: [
+ toRedactedErrorSample('Cannot provision public hosts: pre-seeded app API/schema metadata is unavailable.', {
+ operation: 'dbpm.provision.graphql',
+ }) as RedactedErrorSample,
+ ],
+ };
+ }
+
+ const casePrefix = `${config.benchmarkOwnedPrefix}_${matrixCase.caseId}`.replace(/_/g, '-').toLowerCase();
+ const dbname = appApi.dbname;
+ if (!dbname) {
+ return {
+ createdIds,
+ errors: [
+ toRedactedErrorSample('Cannot provision public hosts: app API dbname is empty.', {
+ operation: 'dbpm.provision.graphql',
+ }) as RedactedErrorSample,
+ ],
+ };
+ }
+
+ for (let index = 0; index < needed; index += 1) {
+ const apiName = `${casePrefix}-api-${index + 1}`.slice(0, 120);
+ const createApi = await executeGraphql<{ createApi?: { api?: ApiNode } }>(context, {
+ query: createApiMutation,
+ variables: {
+ input: {
+ api: {
+ databaseId: DEFAULT_DATABASE_ID,
+ name: apiName,
+ dbname,
+ roleName: appApi.roleName || 'authenticated',
+ anonRole: appApi.anonRole || 'anonymous',
+ isPublic: true,
+ },
+ },
+ },
+ headers: publicHostHeaders(PUBLIC_HOST),
+ });
+
+ if (responseHasUnexpectedErrors(createApi) || !createApi.body.data?.createApi?.api?.id) {
+ errors.push(responseErrorSample(createApi, { operation: 'dbpm.provision.createApi' }));
+ break;
+ }
+
+ const api = createApi.body.data.createApi.api;
+ createdIds.push(api.id);
+
+ for (const apiSchema of appSchemas) {
+ const createApiSchema = await executeGraphql(context, {
+ query: createApiSchemaMutation,
+ variables: {
+ input: {
+ apiSchema: {
+ databaseId: DEFAULT_DATABASE_ID,
+ apiId: api.id,
+ schemaId: apiSchema.schemaId,
+ },
+ },
+ },
+ headers: publicHostHeaders(PUBLIC_HOST),
+ });
+ if (responseHasUnexpectedErrors(createApiSchema)) {
+ errors.push(responseErrorSample(createApiSchema, { operation: 'dbpm.provision.createApiSchema' }));
+ break;
+ }
+ }
+
+ const createDomain = await executeGraphql<{ createDomain?: { domain?: DomainNode } }>(context, {
+ query: createDomainMutation,
+ variables: {
+ input: {
+ domain: {
+ databaseId: DEFAULT_DATABASE_ID,
+ apiId: api.id,
+ subdomain: `${casePrefix}-${index + 1}`.slice(0, 120),
+ domain: 'constructive.io',
+ },
+ },
+ },
+ headers: publicHostHeaders(PUBLIC_HOST),
+ });
+
+ if (responseHasUnexpectedErrors(createDomain) || !createDomain.body.data?.createDomain?.domain?.id) {
+ errors.push(responseErrorSample(createDomain, { operation: 'dbpm.provision.createDomain' }));
+ break;
+ }
+ createdIds.push(createDomain.body.data.createDomain.domain.id);
+ }
+
+ return { createdIds, errors };
+};
+
+export const provisionDbpmPublic = async (input: {
+ context: BenchmarkContext;
+ matrixCase: MatrixCase;
+ config: PerfRunConfig;
+}): Promise => {
+ const { context, matrixCase, config } = input;
+ const errors: RedactedErrorSample[] = [];
+ const warnings: string[] = [];
+
+ // The server-test seed establishes one public app host before the benchmark
+ // server starts. That satisfies tiny public smoke without mutating shared
+ // metadata during preflight. Larger k still requires actual DBPM provisioning
+ // and must fail clearly if the current GraphQL surface cannot provide it.
+ if (matrixCase.scaleProfile.k <= 1) {
+ return {
+ ok: true,
+ completed: true,
+ source: 'fixture-preseeded',
+ tenantCount: 1,
+ publicHostCount: 1,
+ apiCount: 1,
+ businessTableCount: 1,
+ requestProfiles: [
+ {
+ id: 'public-app',
+ routingMode: 'public',
+ routeKey: PUBLIC_HOST,
+ headers: publicHostHeaders(PUBLIC_HOST),
+ description: 'Pre-seeded public app host route from simple-seed-services.',
+ benchmarkOwned: false,
+ metadata: { host: PUBLIC_HOST, databaseId: DEFAULT_DATABASE_ID, source: 'simple-seed-services' },
+ },
+ ],
+ operationProfiles: buildPublicOperationProfiles(matrixCase.workloadProfile),
+ benchmarkOwnedObjectIds: [],
+ errors,
+ warnings,
+ };
+ }
+
+ // Public-mode servers cannot use X-Meta-Schema by design, so the first-version
+ // helper attempts GraphQL DBPM mutations through the pre-seeded public host when
+ // more profiles are required. If those mutations are not exposed by the current
+ // product schema, the report fails clearly instead of fabricating tenants.
+ const snapshotResult = await querySnapshotViaGraphql(context, publicHostHeaders(PUBLIC_HOST));
+
+ if (!snapshotResult.snapshot) {
+ warnings.push(
+ 'Public host does not expose DBPM metadata GraphQL; using the pre-seeded public app host as the first-version fixture-backed route profile.'
+ );
+ warnings.push(...snapshotResult.errors.map((error) => error.message));
+ const fallbackProfile: RequestProfile = {
+ id: 'public-app',
+ routingMode: 'public',
+ routeKey: PUBLIC_HOST,
+ headers: publicHostHeaders(PUBLIC_HOST),
+ description: 'Pre-seeded public app host route from simple-seed-services; DBPM metadata is not exposed on public app schema.',
+ benchmarkOwned: false,
+ metadata: { host: PUBLIC_HOST, databaseId: DEFAULT_DATABASE_ID, source: 'simple-seed-services' },
+ };
+ return {
+ ok: matrixCase.scaleProfile.k <= 1,
+ completed: matrixCase.scaleProfile.k <= 1,
+ source: 'fixture-preseeded',
+ tenantCount: 1,
+ publicHostCount: 1,
+ apiCount: 1,
+ businessTableCount: 1,
+ requestProfiles: [fallbackProfile],
+ operationProfiles: buildPublicOperationProfiles(matrixCase.workloadProfile),
+ benchmarkOwnedObjectIds: [],
+ errors: matrixCase.scaleProfile.k <= 1 ? [] : snapshotResult.errors,
+ warnings,
+ };
+ }
+
+ errors.push(...snapshotResult.errors);
+ let snapshot = snapshotResult.snapshot;
+ let requestProfiles = publicProfilesFromSnapshot(snapshot, config, matrixCase);
+ let source: DbpmProvisionResult['source'] = 'fixture-preseeded';
+ const needed = Math.max(0, matrixCase.scaleProfile.k - requestProfiles.length);
+ const createdIds: string[] = [];
+
+ if (needed > 0) {
+ const provision = await tryGraphqlProvision({ context, matrixCase, config, snapshot, needed });
+ createdIds.push(...provision.createdIds);
+ errors.push(...provision.errors);
+ source = provision.createdIds.length > 0 ? 'mixed' : 'unavailable';
+
+ if (provision.createdIds.length > 0) {
+ const refreshed = await querySnapshotViaGraphql(context, publicHostHeaders(PUBLIC_HOST));
+ errors.push(...refreshed.errors);
+ if (refreshed.snapshot) snapshot = refreshed.snapshot;
+ requestProfiles = publicProfilesFromSnapshot(snapshot, config, matrixCase);
+ }
+
+ if (requestProfiles.length < matrixCase.scaleProfile.k) {
+ warnings.push(
+ `Requested k=${matrixCase.scaleProfile.k} public profiles but only ${requestProfiles.length} are reachable via current GraphQL surface.`
+ );
+ }
+ }
+
+ const publicApiIds = new Set((snapshot.apis?.nodes || []).filter((api) => api.isPublic).map((api) => api.id));
+ const publicHostCount = (snapshot.domains?.nodes || []).filter((domain) => domain.apiId && publicApiIds.has(domain.apiId)).length;
+
+ return {
+ ok: errors.length === 0,
+ completed: errors.length === 0,
+ source,
+ tenantCount: new Set(requestProfiles.map((profile) => String(profile.metadata.databaseId || DEFAULT_DATABASE_ID))).size,
+ publicHostCount: requestProfiles.length || publicHostCount,
+ apiCount: publicApiIds.size,
+ businessTableCount: 1,
+ requestProfiles,
+ operationProfiles: buildPublicOperationProfiles(matrixCase.workloadProfile),
+ benchmarkOwnedObjectIds: createdIds,
+ 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..01a38e38f7
--- /dev/null
+++ b/graphql/server-test/perf/src/gates.ts
@@ -0,0 +1,59 @@
+import type { ResetResult } from './reset';
+import type {
+ LoadReport,
+ MemoryCaptureResult,
+ PublicPreflightResult,
+ RouteProbeSummary,
+} from './types';
+
+export const evaluateCaseGates = (input: {
+ preflight?: PublicPreflightResult;
+ routeProbe: RouteProbeSummary;
+ load?: LoadReport;
+ memoryBefore?: MemoryCaptureResult;
+ memoryAfter?: MemoryCaptureResult;
+ resetBefore?: ResetResult;
+ resetAfter?: ResetResult;
+ reportWritten?: boolean;
+ captureMemory: boolean;
+}): { hardGateFailures: string[]; observations: Record } => {
+ const failures: string[] = [];
+ const { preflight, routeProbe, load, memoryBefore, memoryAfter, resetBefore, resetAfter } = input;
+
+ if (preflight && !preflight.ok) {
+ failures.push(...preflight.hardGateFailures.map((failure) => `preflight: ${failure}`));
+ }
+ if (!routeProbe.ok) failures.push('route probes failed');
+ if (routeProbe.unexpectedGraphqlErrors !== 0) failures.push('route probes returned unexpected GraphQL errors');
+
+ if (load) {
+ if (load.failedRequests !== 0) failures.push(`measured load had ${load.failedRequests} failed requests`);
+ if (load.unexpectedGraphqlErrors !== 0) {
+ failures.push(`measured load had ${load.unexpectedGraphqlErrors} unexpected GraphQL errors`);
+ }
+ if (load.failFast?.triggered) failures.push(`fail-fast triggered: ${load.failFast.reason || 'unknown reason'}`);
+ }
+
+ if (input.captureMemory) {
+ if (memoryBefore && !memoryBefore.ok) failures.push(`memory before snapshot ${memoryBefore.status}`);
+ if (memoryAfter && !memoryAfter.ok) failures.push(`memory after snapshot ${memoryAfter.status}`);
+ }
+
+ if (resetBefore && !resetBefore.ok) failures.push('benchmark-owned reset before load failed');
+ if (resetAfter && !resetAfter.ok) failures.push('benchmark-owned reset after load failed');
+ if (input.reportWritten === false) failures.push('case report was not written');
+
+ return {
+ hardGateFailures: failures,
+ observations: {
+ qps: load?.qps,
+ latencyMs: load?.latencyMs,
+ totalRequests: load?.totalRequests,
+ failedRequests: load?.failedRequests,
+ memoryBeforeStatus: memoryBefore?.status,
+ memoryAfterStatus: memoryAfter?.status,
+ resetBeforeDeletedRows: resetBefore?.deletedRows,
+ resetAfterDeletedRows: resetAfter?.deletedRows,
+ },
+ };
+};
diff --git a/graphql/server-test/perf/src/load.ts b/graphql/server-test/perf/src/load.ts
new file mode 100644
index 0000000000..e0586b2635
--- /dev/null
+++ b/graphql/server-test/perf/src/load.ts
@@ -0,0 +1,76 @@
+import { executeOperation, compatibleOperationsFor } from './operations';
+import { summarizeOutcomes } from './stats';
+import type {
+ BenchmarkContext,
+ LoadReport,
+ MatrixCase,
+ PerfRunConfig,
+ RequestOutcome,
+ RequestProfile,
+ RuntimeOperationProfile,
+} from './types';
+
+const shouldFailFast = (outcomes: RequestOutcome[]): string | undefined => {
+ const failed = outcomes.filter((outcome) => !outcome.ok).length;
+ const graphqlErrors = outcomes.reduce((sum, outcome) => sum + outcome.unexpectedGraphqlErrors, 0);
+ const httpFailures = outcomes.filter((outcome) => outcome.status != null && (outcome.status < 200 || outcome.status >= 300)).length;
+ const networkFailures = outcomes.filter((outcome) => outcome.status == null && !outcome.ok).length;
+ const total = outcomes.length;
+
+ if (networkFailures >= 5) return `network failures reached ${networkFailures}`;
+ if (httpFailures >= 5) return `HTTP failures reached ${httpFailures}`;
+ if (graphqlErrors >= 5) return `unexpected GraphQL errors reached ${graphqlErrors}`;
+ if (total >= 10 && failed / total > 0.5) return `failure rate exceeded 50% (${failed}/${total})`;
+ return undefined;
+};
+
+export const runLoad = async (input: {
+ context: BenchmarkContext;
+ matrixCase: MatrixCase;
+ config: PerfRunConfig;
+ requestProfiles: RequestProfile[];
+ operationProfiles: RuntimeOperationProfile[];
+}): Promise => {
+ const { context, matrixCase, config, requestProfiles, operationProfiles } = input;
+ const durationSeconds = matrixCase.scaleProfile.durationSeconds;
+ const workers = Math.max(1, matrixCase.scaleProfile.workers);
+ const stopAt = Date.now() + durationSeconds * 1000;
+ const outcomes: RequestOutcome[] = [];
+ let sequence = 0;
+ let failFast: { triggered: boolean; reason?: string } = { triggered: false };
+
+ const worker = async (workerId: number): Promise => {
+ while (Date.now() < stopAt && !failFast.triggered) {
+ const current = sequence++;
+ const requestProfile = requestProfiles[current % requestProfiles.length];
+ const compatible = compatibleOperationsFor(requestProfile, operationProfiles);
+ const bag = compatible.flatMap((operation) => Array(Math.max(1, Math.floor(operation.weight))).fill(operation));
+ const operation = bag[(current + workerId) % bag.length];
+ if (!operation) break;
+ const outcome = await executeOperation({
+ context,
+ requestProfile,
+ operation,
+ sequence: current,
+ config,
+ matrixCase,
+ });
+ outcomes.push(outcome);
+
+ if (config.failFast) {
+ const reason = shouldFailFast(outcomes);
+ if (reason) failFast = { triggered: true, reason };
+ }
+ }
+ };
+
+ await Promise.all(Array.from({ length: workers }, (_, index) => worker(index)));
+
+ return summarizeOutcomes({
+ outcomes,
+ durationSeconds,
+ workers,
+ failFast,
+ errorSampleLimit: config.errorSampleLimit,
+ });
+};
diff --git a/graphql/server-test/perf/src/matrix.ts b/graphql/server-test/perf/src/matrix.ts
new file mode 100644
index 0000000000..b2260d9f23
--- /dev/null
+++ b/graphql/server-test/perf/src/matrix.ts
@@ -0,0 +1,320 @@
+import {
+ caseReportPath,
+ createRunArtifacts,
+ errorsReportPath,
+ writeJsonArtifact,
+} from './artifacts';
+import { BenchmarkContextManager } from './context';
+import { evaluateCaseGates } from './gates';
+import { runLoad } from './load';
+import { captureMemorySnapshot } from './memory';
+import { buildPrivateProfiles } from './profiles';
+import { probeProfiles, runPublicPreflight } from './preflight';
+import { resetBenchmarkOwnedData, type ResetResult } from './reset';
+import type {
+ BenchmarkContext,
+ CaseReport,
+ CaseSummary,
+ MatrixCase,
+ PerfRunConfig,
+ PerfRunSummary,
+ PublicPreflightResult,
+ RequestProfile,
+ RouteProbeSummary,
+ RunArtifactPaths,
+ RuntimeOperationProfile,
+} from './types';
+import { PERF_SCHEMA_VERSION } from './types';
+
+const routeOrder: Record = { private: 0, public: 1 };
+const cacheOrder: Record = { old: 0, new: 1 };
+
+const safeCasePart = (value: string): string => value.replace(/[^a-zA-Z0-9-]+/g, '-').replace(/^-|-$/g, '');
+
+const caseIdFor = (matrixCase: Omit): string =>
+ [
+ matrixCase.routingMode,
+ matrixCase.cacheMode,
+ matrixCase.scaleProfile.name,
+ matrixCase.workloadProfile,
+ ].map(safeCasePart).join('__');
+
+export const expandMatrixCases = (config: PerfRunConfig): MatrixCase[] => {
+ const cases: MatrixCase[] = [];
+ const routingModes = [...config.routingModes].sort((a, b) => routeOrder[a] - routeOrder[b]);
+ const cacheModes = [...config.cacheModes].sort((a, b) => cacheOrder[a] - cacheOrder[b]);
+
+ for (const routingMode of routingModes) {
+ for (const cacheMode of cacheModes) {
+ const workloadProfile = config.workloadProfiles[routingMode];
+ const base = {
+ routingMode,
+ cacheMode,
+ scaleProfile: config.scaleProfile,
+ workloadProfile,
+ };
+ cases.push({
+ ...base,
+ caseId: caseIdFor(base),
+ mutates: routingMode === 'public' && /crud|write|mutat/i.test(workloadProfile),
+ });
+ }
+ }
+
+ 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 writeErrorsIfAny = async (input: {
+ artifacts: RunArtifactPaths;
+ config: PerfRunConfig;
+ caseId: string;
+ routeProbe?: RouteProbeSummary;
+ loadErrors?: unknown[];
+ preflight?: PublicPreflightResult;
+}): Promise => {
+ const errors = [
+ ...(input.preflight?.routeProbe.errorSamples || []),
+ ...(input.routeProbe?.errorSamples || []),
+ ...(input.loadErrors || []),
+ ].slice(0, input.config.errorSampleLimit);
+
+ if (errors.length === 0) return undefined;
+ const path = errorsReportPath(input.artifacts, input.caseId);
+ await writeJsonArtifact(path, {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId: input.config.runId,
+ caseId: input.caseId,
+ writtenAt: new Date().toISOString(),
+ errors,
+ });
+ return path;
+};
+
+const toCaseSummary = (report: CaseReport): CaseSummary => ({
+ caseId: report.caseId,
+ ok: report.ok,
+ status: report.status,
+ routingMode: report.matrix.routingMode,
+ cacheMode: report.matrix.cacheMode,
+ scaleProfile: report.matrix.scaleProfile.name,
+ workloadProfile: report.matrix.workloadProfile,
+ contextId: report.lifecycle.contextId,
+ reportPath: report.artifacts.caseReportPath,
+ hardGateFailures: report.gates.hardGateFailures,
+ totalRequests: report.load?.totalRequests,
+ qps: report.load?.qps,
+});
+
+const executeCase = async (input: {
+ config: PerfRunConfig;
+ matrixCase: MatrixCase;
+ contextManager: BenchmarkContextManager;
+ artifacts: RunArtifactPaths;
+}): Promise => {
+ const { config, matrixCase, contextManager, artifacts } = input;
+ const startedAt = new Date().toISOString();
+ let context: BenchmarkContext | undefined;
+ let requestProfiles: RequestProfile[] = [];
+ let operationProfiles: RuntimeOperationProfile[] = [];
+ let preflight: PublicPreflightResult | undefined;
+ let routeProbe: RouteProbeSummary = emptyRouteProbe('case did not reach route probing');
+ let resetBefore: ResetResult | undefined;
+ let resetAfter: ResetResult | undefined;
+ let memoryBefore;
+ let memoryAfter;
+ let load;
+ let reportWritten = false;
+ let caseReportFile = caseReportPath(artifacts, matrixCase.caseId);
+ let errorsPath: string | undefined;
+ let thrownFailure: string | undefined;
+
+ try {
+ context = await contextManager.acquire(matrixCase);
+
+ if (matrixCase.routingMode === 'public') {
+ preflight = await runPublicPreflight({ context, matrixCase, config, artifacts });
+ requestProfiles = preflight.requestProfiles;
+ operationProfiles = preflight.operationProfiles;
+ if (!preflight.ok) {
+ routeProbe = preflight.routeProbe;
+ }
+ } else {
+ const privateProfiles = buildPrivateProfiles(matrixCase, config);
+ requestProfiles = privateProfiles.requestProfiles;
+ operationProfiles = privateProfiles.operationProfiles;
+ }
+
+ if (!preflight || preflight.ok) {
+ resetBefore = await resetBenchmarkOwnedData({ context, matrixCase, config });
+ memoryBefore = await captureMemorySnapshot({
+ context,
+ config,
+ artifacts,
+ caseId: matrixCase.caseId,
+ phase: 'before',
+ });
+ routeProbe = await probeProfiles({ context, matrixCase, config, requestProfiles, operationProfiles });
+ if (routeProbe.ok) {
+ load = await runLoad({ context, matrixCase, config, requestProfiles, operationProfiles });
+ }
+ memoryAfter = await captureMemorySnapshot({
+ context,
+ config,
+ artifacts,
+ caseId: matrixCase.caseId,
+ phase: 'after',
+ });
+ resetAfter = await resetBenchmarkOwnedData({ context, matrixCase, config });
+ }
+ } catch (error) {
+ thrownFailure = error instanceof Error ? error.message : String(error);
+ routeProbe = routeProbe.ok ? routeProbe : emptyRouteProbe(thrownFailure);
+ } finally {
+ if (context) {
+ await contextManager.releaseCase(context);
+ }
+ }
+
+ const gates = evaluateCaseGates({
+ preflight,
+ routeProbe,
+ load,
+ memoryBefore,
+ memoryAfter,
+ resetBefore,
+ resetAfter,
+ reportWritten: true,
+ captureMemory: config.captureMemory,
+ });
+ if (thrownFailure) gates.hardGateFailures.unshift(`case threw before completion: ${thrownFailure}`);
+
+ errorsPath = await writeErrorsIfAny({
+ artifacts,
+ config,
+ caseId: matrixCase.caseId,
+ routeProbe,
+ loadErrors: load?.errorSamples,
+ preflight,
+ });
+
+ const report: CaseReport = {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId: config.runId,
+ caseId: matrixCase.caseId,
+ startedAt,
+ finishedAt: new Date().toISOString(),
+ ok: gates.hardGateFailures.length === 0,
+ status: gates.hardGateFailures.length === 0 ? '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 || contextManager.compatibilityKeyFor(matrixCase),
+ serverUrl: context?.serverUrl || 'unavailable',
+ },
+ preflight: preflight
+ ? {
+ ok: preflight.ok,
+ artifactPath: preflight.artifactPath,
+ provisionedTenantCount: preflight.provisionedTenantCount,
+ publicHostCount: preflight.publicHostCount,
+ requestProfileCount: preflight.requestProfileCount,
+ operationProfileCount: preflight.operationProfileCount,
+ hardGateFailures: preflight.hardGateFailures,
+ }
+ : undefined,
+ routeProbe,
+ load,
+ memory: {
+ beforePath: memoryBefore?.path,
+ afterPath: memoryAfter?.path,
+ beforeOk: memoryBefore?.ok ?? !config.captureMemory,
+ afterOk: memoryAfter?.ok ?? !config.captureMemory,
+ beforeStatus: memoryBefore?.status,
+ afterStatus: memoryAfter?.status,
+ },
+ gates,
+ artifacts: {
+ caseReportPath: caseReportFile,
+ preflightPath: preflight?.artifactPath,
+ memoryBeforePath: memoryBefore?.path,
+ memoryAfterPath: memoryAfter?.path,
+ errorsPath,
+ },
+ };
+
+ await writeJsonArtifact(caseReportFile, report);
+ reportWritten = true;
+ void reportWritten;
+ return report;
+};
+
+export const runPerfMatrix = async (config: PerfRunConfig): Promise => {
+ const startedAt = new Date().toISOString();
+ const artifacts = await createRunArtifacts(config);
+ const cases = expandMatrixCases(config);
+ const contextManager = new BenchmarkContextManager(config);
+ const caseSummaries: CaseSummary[] = [];
+
+ try {
+ for (const matrixCase of cases) {
+ const report = await executeCase({ config, matrixCase, contextManager, artifacts });
+ caseSummaries.push(toCaseSummary(report));
+ }
+ } finally {
+ await contextManager.teardownAll();
+ }
+
+ const passed = caseSummaries.filter((item) => item.status === 'passed').length;
+ const failed = caseSummaries.filter((item) => item.status === 'failed').length;
+ const skipped = caseSummaries.filter((item) => item.status === 'skipped').length;
+ const summary: PerfRunSummary = {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId: config.runId,
+ runDir: artifacts.runDir,
+ configGroup: config.configGroup,
+ specPath: config.specPath,
+ startedAt,
+ finishedAt: new Date().toISOString(),
+ pass: failed === 0,
+ config,
+ totals: {
+ caseCount: caseSummaries.length,
+ passed,
+ failed,
+ skipped,
+ },
+ cases: caseSummaries,
+ 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..423cc7ee3f
--- /dev/null
+++ b/graphql/server-test/perf/src/memory.ts
@@ -0,0 +1,91 @@
+import { memoryReportPath, toRedactedErrorSample, writeJsonArtifact } from './artifacts';
+import type {
+ BenchmarkContext,
+ MemoryCaptureResult,
+ MemoryPhase,
+ MemorySnapshotArtifact,
+ PerfRunConfig,
+ RedactedErrorSample,
+ RunArtifactPaths,
+} from './types';
+import { PERF_SCHEMA_VERSION } from './types';
+
+export const captureMemorySnapshot = async (input: {
+ context: BenchmarkContext;
+ config: PerfRunConfig;
+ artifacts: RunArtifactPaths;
+ caseId: string;
+ phase: MemoryPhase;
+}): Promise => {
+ const { context, config, artifacts, caseId, phase } = input;
+ const filePath = memoryReportPath(artifacts, caseId, phase);
+
+ if (!config.captureMemory) {
+ const payload: MemorySnapshotArtifact = {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId: config.runId,
+ caseId,
+ phase,
+ capturedAt: new Date().toISOString(),
+ ok: true,
+ status: 'disabled',
+ serverUrl: context.serverUrl,
+ };
+ await writeJsonArtifact(filePath, payload);
+ return { ok: true, status: 'disabled', path: filePath };
+ }
+
+ try {
+ const response = await context.conn.request.get('/debug/memory');
+ if (response.status === 200) {
+ const payload: MemorySnapshotArtifact = {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId: config.runId,
+ caseId,
+ phase,
+ capturedAt: new Date().toISOString(),
+ ok: true,
+ status: 'captured',
+ serverUrl: context.serverUrl,
+ httpStatus: response.status,
+ snapshot: response.body,
+ };
+ await writeJsonArtifact(filePath, payload);
+ return { ok: true, status: 'captured', path: filePath };
+ }
+
+ const error = toRedactedErrorSample(response.text || `HTTP ${response.status}`, {
+ operation: 'memory.capture',
+ status: response.status,
+ }) as RedactedErrorSample;
+ const payload: MemorySnapshotArtifact = {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId: config.runId,
+ caseId,
+ phase,
+ capturedAt: new Date().toISOString(),
+ ok: false,
+ status: 'unavailable',
+ serverUrl: context.serverUrl,
+ httpStatus: response.status,
+ error,
+ };
+ await writeJsonArtifact(filePath, payload);
+ return { ok: false, status: 'unavailable', path: filePath, error };
+ } catch (err) {
+ const error = toRedactedErrorSample(err, { operation: 'memory.capture' }) as RedactedErrorSample;
+ const payload: MemorySnapshotArtifact = {
+ schemaVersion: PERF_SCHEMA_VERSION,
+ runId: config.runId,
+ caseId,
+ phase,
+ capturedAt: new Date().toISOString(),
+ ok: false,
+ status: 'failed',
+ serverUrl: context.serverUrl,
+ error,
+ };
+ await writeJsonArtifact(filePath, payload);
+ return { ok: false, status: 'failed', path: filePath, error };
+ }
+};
diff --git a/graphql/server-test/perf/src/operations.ts b/graphql/server-test/perf/src/operations.ts
new file mode 100644
index 0000000000..b04fd80eb7
--- /dev/null
+++ b/graphql/server-test/perf/src/operations.ts
@@ -0,0 +1,254 @@
+import type { Response } from 'supertest';
+
+import { toRedactedErrorSample } from './artifacts';
+import type {
+ BenchmarkContext,
+ MatrixCase,
+ PerfRunConfig,
+ RedactedErrorSample,
+ RequestOutcome,
+ RequestProfile,
+ RuntimeOperationProfile,
+} from './types';
+
+export const DEFAULT_DATABASE_ID = '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9';
+export const SIMPLE_PETS_SCHEMAS = ['simple-pets-public', 'simple-pets-pets-public'];
+export const META_SCHEMAS = ['services_public', 'metaschema_public', 'metaschema_modules_public'];
+export const PUBLIC_HOST = 'app.test.constructive.io';
+export const PRIVATE_API_NAME = 'private';
+export const PRIVATE_SCHEMATA = SIMPLE_PETS_SCHEMAS.join(',');
+export const BENCHMARK_ANIMAL_ID = 'a0000001-0000-0000-0000-000000000001';
+
+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;
+ }
+): Promise> => {
+ let req = context.conn.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 privateSchemataHeaders = (): Record => ({
+ 'X-Database-Id': DEFAULT_DATABASE_ID,
+ 'X-Schemata': PRIVATE_SCHEMATA,
+});
+
+export const publicHostHeaders = (host = PUBLIC_HOST): Record => ({ Host: host });
+
+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 animalsListQuery = `query PerfAnimalsList($first: Int!) {
+ animals(first: $first) { nodes { id name species } }
+}`;
+
+const createAnimalMutation = `mutation PerfCreateAnimal($input: CreateAnimalInput!) {
+ createAnimal(input: $input) { animal { id name species } }
+}`;
+
+const updateAnimalMutation = `mutation PerfUpdateAnimal($input: UpdateAnimalInput!) {
+ updateAnimal(input: $input) { animal { id name species } }
+}`;
+
+export const buildPrivateOperationProfiles = (workloadProfile: string): RuntimeOperationProfile[] => [
+ {
+ id: 'private-animals-list',
+ name: 'private.animals.list',
+ weight: 5,
+ workloadProfile,
+ description: 'Read app animals through private service routing.',
+ mutates: false,
+ compatibleRequestProfileIds: ['private-api', 'private-schemata'],
+ query: animalsListQuery,
+ variables: { first: 5 },
+ },
+ {
+ id: 'private-typename',
+ name: 'private.typename',
+ weight: 2,
+ workloadProfile,
+ description: 'Tiny private schema sanity query.',
+ mutates: false,
+ compatibleRequestProfileIds: ['private-api', 'private-schemata'],
+ query: '{ __typename }',
+ },
+ {
+ id: 'private-metadata-read',
+ name: 'private.metadata.read',
+ weight: 3,
+ workloadProfile,
+ description: 'Read metadata through X-Meta-Schema private route.',
+ mutates: false,
+ compatibleRequestProfileIds: ['private-meta'],
+ query: metadataQuery,
+ },
+];
+
+export const buildPublicOperationProfiles = (workloadProfile: string): RuntimeOperationProfile[] => [
+ {
+ id: 'public-animals-list',
+ name: 'public.animals.list',
+ weight: 8,
+ workloadProfile,
+ description: 'List business animals through public host routing.',
+ mutates: false,
+ query: animalsListQuery,
+ variables: { first: 5 },
+ },
+ {
+ id: 'public-animal-create',
+ name: 'public.animals.create',
+ weight: 1,
+ workloadProfile,
+ description: 'Create benchmark-owned business animal rows through GraphQL.',
+ mutates: true,
+ query: createAnimalMutation,
+ buildVariables: ({ sequence, config, matrixCase, requestProfile }) => ({
+ input: {
+ animal: {
+ name: `${config.benchmarkOwnedPrefix}_${matrixCase.caseId}_${requestProfile.id}_${sequence}`.slice(0, 240),
+ species: 'Perf',
+ },
+ },
+ }),
+ },
+ {
+ id: 'public-animal-update',
+ name: 'public.animals.update',
+ weight: 1,
+ workloadProfile,
+ description: 'Update a benchmark-owned business animal through GraphQL.',
+ mutates: true,
+ query: updateAnimalMutation,
+ buildVariables: ({ sequence, config, matrixCase, requestProfile }) => ({
+ input: {
+ id: requestProfile.metadata.benchmarkAnimalId,
+ animalPatch: {
+ name: `${config.benchmarkOwnedPrefix}_${matrixCase.caseId}_${requestProfile.id}_update_${sequence}`.slice(0, 240),
+ },
+ },
+ }),
+ },
+];
+
+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;
+ config: PerfRunConfig;
+ matrixCase: MatrixCase;
+}): Promise => {
+ const { context, requestProfile, operation, sequence, config, matrixCase } = input;
+ const started = performance.now();
+ let response: GraphqlHttpResult | undefined;
+ try {
+ const variables = operation.buildVariables
+ ? operation.buildVariables({ sequence, requestProfile, config, matrixCase })
+ : operation.variables;
+ response = await executeGraphql(context, {
+ query: operation.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,
+ 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..17ddb10934
--- /dev/null
+++ b/graphql/server-test/perf/src/preflight.ts
@@ -0,0 +1,179 @@
+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 operation = compatibleOperationsFor(requestProfile, operationProfiles).find((candidate) => !candidate.mutates)
+ || compatibleOperationsFor(requestProfile, operationProfiles)[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 (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,
+ },
+ 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,
+ };
+};
diff --git a/graphql/server-test/perf/src/profiles.ts b/graphql/server-test/perf/src/profiles.ts
new file mode 100644
index 0000000000..002d951d72
--- /dev/null
+++ b/graphql/server-test/perf/src/profiles.ts
@@ -0,0 +1,102 @@
+import {
+ buildPrivateOperationProfiles,
+ buildPublicOperationProfiles,
+ metaHeaders,
+ privateApiHeaders,
+ privateSchemataHeaders,
+ publicHostHeaders,
+ PUBLIC_HOST,
+} from './operations';
+import type {
+ DbpmProvisionResult,
+ MatrixCase,
+ PerfRunConfig,
+ RequestProfile,
+ RuntimeOperationProfile,
+} from './types';
+
+const basePrivateProfiles = (): RequestProfile[] => [
+ {
+ id: 'private-api',
+ routingMode: 'private',
+ routeKey: 'api:simple-pets:private',
+ headers: privateApiHeaders(),
+ description: 'Private services API route selected by X-Api-Name.',
+ benchmarkOwned: false,
+ metadata: { source: 'simple-seed-services' },
+ },
+ {
+ id: 'private-schemata',
+ routingMode: 'private',
+ routeKey: 'schemata:simple-pets-public,simple-pets-pets-public',
+ headers: privateSchemataHeaders(),
+ description: 'Private direct schema route selected by X-Schemata.',
+ benchmarkOwned: false,
+ metadata: { source: 'simple-seed-services' },
+ },
+ {
+ id: 'private-meta',
+ routingMode: 'private',
+ routeKey: 'metaschema:simple-pets',
+ headers: metaHeaders(),
+ description: 'Private metadata route selected by X-Meta-Schema.',
+ benchmarkOwned: false,
+ metadata: { source: 'simple-seed-services' },
+ },
+];
+
+export const buildPrivateProfiles = (
+ matrixCase: MatrixCase,
+ config: PerfRunConfig
+): { requestProfiles: RequestProfile[]; operationProfiles: RuntimeOperationProfile[] } => {
+ 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 },
+ }
+ );
+ }
+
+ const operations = buildPrivateOperationProfiles(matrixCase.workloadProfile).flatMap((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.length
+ ? provision.requestProfiles
+ : [
+ {
+ id: 'public-app',
+ routingMode: 'public',
+ routeKey: PUBLIC_HOST,
+ headers: publicHostHeaders(),
+ description: 'Public app host route from simple-seed-services.',
+ benchmarkOwned: false,
+ metadata: { host: PUBLIC_HOST, source: 'simple-seed-services' },
+ },
+ ],
+ 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..3209f22ef5
--- /dev/null
+++ b/graphql/server-test/perf/src/reset.ts
@@ -0,0 +1,35 @@
+import { toRedactedErrorSample } from './artifacts';
+import type { BenchmarkContext, MatrixCase, PerfRunConfig, RedactedErrorSample } from './types';
+
+export interface ResetResult {
+ ok: boolean;
+ skipped: boolean;
+ deletedRows?: number;
+ errors: RedactedErrorSample[];
+}
+
+export const resetBenchmarkOwnedData = async (input: {
+ context: BenchmarkContext;
+ matrixCase: MatrixCase;
+ config: PerfRunConfig;
+}): Promise => {
+ const { context, matrixCase, config } = input;
+ if (!matrixCase.mutates) return { ok: true, skipped: true, errors: [] };
+
+ const prefix = `${config.benchmarkOwnedPrefix}_${matrixCase.caseId}_%`;
+ try {
+ const result = await context.conn.pg.query(
+ 'DELETE FROM "simple-pets-pets-public".animals WHERE name LIKE $1',
+ [prefix]
+ );
+ return { ok: true, skipped: false, deletedRows: result.rowCount ?? 0, errors: [] };
+ } catch (error) {
+ return {
+ ok: false,
+ skipped: false,
+ errors: [
+ toRedactedErrorSample(error, { operation: 'reset.benchmarkOwnedAnimals' }) as RedactedErrorSample,
+ ],
+ };
+ }
+};
diff --git a/graphql/server-test/perf/src/setup.ts b/graphql/server-test/perf/src/setup.ts
new file mode 100644
index 0000000000..2ba942f853
--- /dev/null
+++ b/graphql/server-test/perf/src/setup.ts
@@ -0,0 +1,78 @@
+import { toRedactedErrorSample } from './artifacts';
+import { executeGraphql } from './operations';
+import type {
+ BenchmarkContext,
+ MatrixCase,
+ PerfRunConfig,
+ RedactedErrorSample,
+ RequestProfile,
+} from './types';
+
+const createAnimalMutation = `mutation PerfSetupCreateAnimal($input: CreateAnimalInput!) {
+ createAnimal(input: $input) { animal { id name species } }
+}`;
+
+export interface BusinessSetupResult {
+ ok: boolean;
+ requestProfiles: RequestProfile[];
+ errors: RedactedErrorSample[];
+ touchedNonBenchmarkObjects: boolean;
+}
+
+export const preparePublicBusinessWorkload = async (input: {
+ context: BenchmarkContext;
+ matrixCase: MatrixCase;
+ config: PerfRunConfig;
+ requestProfiles: RequestProfile[];
+}): Promise => {
+ const { context, matrixCase, config } = input;
+ const errors: RedactedErrorSample[] = [];
+ const prepared: RequestProfile[] = [];
+
+ for (const [index, profile] of input.requestProfiles.entries()) {
+ const name = `${config.benchmarkOwnedPrefix}_${matrixCase.caseId}_${profile.id}_seed_${index}`.slice(0, 240);
+ try {
+ const response = await executeGraphql<{ createAnimal?: { animal?: { id?: string } } }>(context, {
+ query: createAnimalMutation,
+ variables: { input: { animal: { name, species: 'Perf' } } },
+ headers: profile.headers,
+ });
+ const animalId = response.body?.data?.createAnimal?.animal?.id;
+ if (response.status < 200 || response.status >= 300 || response.body?.errors?.length || !animalId) {
+ errors.push(
+ toRedactedErrorSample(response.body?.errors || response.text || 'GraphQL setup did not return animal id', {
+ operation: 'setup.public.createBenchmarkAnimal',
+ requestProfileId: profile.id,
+ status: response.status,
+ }) as RedactedErrorSample
+ );
+ prepared.push(profile);
+ } else {
+ prepared.push({
+ ...profile,
+ benchmarkOwned: profile.benchmarkOwned,
+ metadata: {
+ ...profile.metadata,
+ benchmarkAnimalId: animalId,
+ benchmarkAnimalName: name,
+ },
+ });
+ }
+ } catch (error) {
+ errors.push(
+ toRedactedErrorSample(error, {
+ operation: 'setup.public.createBenchmarkAnimal',
+ requestProfileId: profile.id,
+ }) as RedactedErrorSample
+ );
+ prepared.push(profile);
+ }
+ }
+
+ return {
+ ok: errors.length === 0,
+ requestProfiles: prepared,
+ errors,
+ touchedNonBenchmarkObjects: false,
+ };
+};
diff --git a/graphql/server-test/perf/src/stats.ts b/graphql/server-test/perf/src/stats.ts
new file mode 100644
index 0000000000..f1c651c9a9
--- /dev/null
+++ b/graphql/server-test/perf/src/stats.ts
@@ -0,0 +1,60 @@
+import type { LoadReport, RequestOutcome } from './types';
+
+export const percentile = (values: number[], p: number): number | null => {
+ if (values.length === 0) return null;
+ if (p <= 0) return Math.min(...values);
+ if (p >= 1) return Math.max(...values);
+ const sorted = [...values].sort((a, b) => a - b);
+ const index = Math.max(0, Math.ceil(p * sorted.length) - 1);
+ return sorted[index];
+};
+
+export const summarizeOutcomes = (input: {
+ outcomes: RequestOutcome[];
+ durationSeconds: number;
+ workers: number;
+ failFast?: { triggered: boolean; reason?: string };
+ errorSampleLimit: number;
+}): LoadReport => {
+ const { outcomes, durationSeconds, workers, failFast, errorSampleLimit } = input;
+ const latencies = outcomes.map((outcome) => outcome.latencyMs);
+ const operations: LoadReport['operations'] = {};
+ let failedRequests = 0;
+ let unexpectedGraphqlErrors = 0;
+
+ for (const outcome of outcomes) {
+ operations[outcome.operation] ??= { total: 0, failed: 0 };
+ operations[outcome.operation].total += 1;
+ if (!outcome.ok) {
+ failedRequests += 1;
+ operations[outcome.operation].failed += 1;
+ }
+ unexpectedGraphqlErrors += outcome.unexpectedGraphqlErrors;
+ }
+
+ const totalRequests = outcomes.length;
+ const effectiveDuration = durationSeconds > 0 ? durationSeconds : 1;
+
+ return {
+ ok: failedRequests === 0 && unexpectedGraphqlErrors === 0 && !failFast?.triggered,
+ durationSeconds,
+ workers,
+ totalRequests,
+ failedRequests,
+ unexpectedGraphqlErrors,
+ qps: totalRequests / effectiveDuration,
+ latencyMs: {
+ p50: percentile(latencies, 0.5),
+ p90: percentile(latencies, 0.9),
+ p95: percentile(latencies, 0.95),
+ p99: percentile(latencies, 0.99),
+ max: percentile(latencies, 1),
+ },
+ operations,
+ failFast,
+ errorSamples: outcomes
+ .filter((outcome) => outcome.error)
+ .slice(0, errorSampleLimit)
+ .map((outcome) => outcome.error!),
+ };
+};
diff --git a/graphql/server-test/perf/src/types.ts b/graphql/server-test/perf/src/types.ts
new file mode 100644
index 0000000000..27f08ddb76
--- /dev/null
+++ b/graphql/server-test/perf/src/types.ts
@@ -0,0 +1,361 @@
+import type { GetConnectionsResult } 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 PerfConfigSource {
+ defaults: string;
+ group: string;
+ specPath?: string;
+ envOverrides: Record;
+}
+
+export interface PerfRunConfig {
+ schemaVersion: SchemaVersion;
+ runId: string;
+ name: string;
+ configGroup: string;
+ specPath?: string;
+ runDir: string;
+ connectionPolicy: ConnectionPolicy;
+ routingModes: RoutingMode[];
+ cacheModes: CacheMode[];
+ scaleProfile: ScaleProfile;
+ workloadProfiles: WorkloadProfileSelection;
+ publicPreflight: PublicPreflightConfig;
+ failFast: boolean;
+ captureMemory: boolean;
+ routeProbeSampleSize: number;
+ errorSampleLimit: number;
+ testTimeoutMs: number;
+ benchmarkOwnedPrefix: string;
+ source: PerfConfigSource;
+}
+
+export type PerfRunConfigOverlay = Partial<
+ Omit<
+ PerfRunConfig,
+ | 'schemaVersion'
+ | 'runId'
+ | 'source'
+ | 'benchmarkOwnedPrefix'
+ | 'scaleProfile'
+ | 'workloadProfiles'
+ | 'publicPreflight'
+ >
+> & {
+ scaleProfile?: Partial & { name?: string };
+ workloadProfiles?: Partial;
+ publicPreflight?: Partial;
+};
+
+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;
+}
+
+export interface RequestProfile {
+ id: string;
+ routingMode: RoutingMode;
+ routeKey: string;
+ headers: Record