From da95727425cb63aa1167708d9bc194b647c46256 Mon Sep 17 00:00:00 2001 From: Xetera Date: Mon, 16 Feb 2026 18:24:07 +0300 Subject: [PATCH 1/8] chore: replace `doubleQuote` with `PgIdentifier` --- src/sync/pg-connector.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sync/pg-connector.ts b/src/sync/pg-connector.ts index 193bfcb..a22d704 100644 --- a/src/sync/pg-connector.ts +++ b/src/sync/pg-connector.ts @@ -192,7 +192,10 @@ ORDER BY values: Record, ) { const columnsText = Object.keys(values) - .map((key, i) => `${doubleQuote(key)} = $${i + 1}`) + .map((rawKey, i) => { + const key = PgIdentifier.fromString(rawKey); + return `${key} = $${i + 1}`; + }) .join(" AND "); // TODO: pass the schema along const sqlString = @@ -553,11 +556,4 @@ ORDER BY } } -const PROBABLY_NO_QUOTE_NEEDED = /^[a-z0-9_]+$/; -function doubleQuote(value: string) { - if (PROBABLY_NO_QUOTE_NEEDED.test(value)) { - return value; - } - return `"${value}"`; -} type TableName = string; From f7fb3eb70c7b3d297d846d04da48e50f629e7879 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Sun, 15 Feb 2026 09:17:32 +0400 Subject: [PATCH 2/8] chore(analyzer): migrate from Deno to Node.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Deno runtime with Node.js 24: - postgres.js → pg (node-postgres) with serializeParams, savepoints, queue - Deno.serve → Fastify HTTP server - Deno test → Vitest - Deno file imports → Node.js fs/path - Add pool error handler for DROP DATABASE WITH (FORCE) resilience - Add esbuild bundling with runtime asset copying Co-Authored-By: Claude Opus 4.6 --- deno.json | 51 - deno.lock | 1774 --------- package-lock.json | 5371 ++++++++++++++++++++++++++ package.json | 50 + src/env.ts | 5 +- src/main.ts | 39 +- src/remote/disabled-indexes.test.ts | 36 +- src/remote/disabled-indexes.ts | 8 +- src/remote/gin-indexes.test.ts | 257 +- src/remote/query-loader.test.ts | 424 +- src/remote/query-optimizer.test.ts | 195 +- src/remote/query-optimizer.ts | 8 +- src/remote/remote-controller.test.ts | 224 +- src/remote/remote-controller.ts | 219 +- src/remote/remote.test.ts | 216 +- src/reporters/github/github.ts | 10 +- src/runner.ts | 23 +- src/server/http.ts | 296 +- src/server/rate-limit.ts | 24 - src/shutdown.ts | 5 - src/sql/json.ts | 2 +- src/sql/postgresjs.ts | 150 +- src/sql/recent-query.ts | 1 - src/sync/connectable.test.ts | 7 +- src/sync/errors.ts | 57 +- src/sync/executable.ts | 46 +- src/sync/pg-connector.ts | 17 +- src/sync/schema-link.ts | 207 +- src/sync/sync.test.ts | 6 +- tsconfig.json | 18 + vitest.config.ts | 8 + 31 files changed, 6570 insertions(+), 3184 deletions(-) delete mode 100644 deno.json delete mode 100644 deno.lock create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 src/server/rate-limit.ts create mode 100644 tsconfig.json create mode 100644 vitest.config.ts diff --git a/deno.json b/deno.json deleted file mode 100644 index 23fe2af..0000000 --- a/deno.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/denoland/deno/refs/heads/main/cli/schemas/config-file.v1.json", - "version": "0.1.1", - "unstable": ["raw-imports"], - "nodeModulesDir": "auto", - "permissions": { - "run": { - "sys": true, - "run": true, - "read": true, - "write": true, - "env": true, - "net": true - } - }, - "tasks": { - "start": "deno run --permission-set=run src/main.ts", - "start:dev": "deno run --env-file=.env -A src/main.ts", - "test": "deno test --permission-set=run src", - "dev": "deno run --env-file=.env --permission-set=run --watch src/main.ts" - }, - "imports": { - "@actions/core": "npm:@actions/core@^1.11.1", - "@actions/github": "npm:@actions/github@^6.0.1", - "@libpg-query/parser": "npm:@libpg-query/parser@^17.6.3", - "@opentelemetry/api": "jsr:@opentelemetry/api@^1.9.0", - "@pgsql/types": "npm:@pgsql/types@^17.6.1", - "@query-doctor/core": "npm:@query-doctor/core@^0.4.0", - "@rabbit-company/rate-limiter": "jsr:@rabbit-company/rate-limiter@^3.0.0", - "@std/assert": "jsr:@std/assert@^1.0.14", - "@std/collections": "jsr:@std/collections@^1.1.3", - "@std/data-structures": "jsr:@std/data-structures@^1.0.9", - "@std/fmt": "jsr:@std/fmt@^1.0.8", - "@std/testing": "jsr:@std/testing@^1.0.16", - "@testcontainers/postgresql": "npm:@testcontainers/postgresql@^11.9.0", - "@types/node": "npm:@types/node@^24.9.1", - "@types/nunjucks": "npm:@types/nunjucks@^3.2.6", - "async-sema": "npm:async-sema@^3.1.1", - "chokidar": "npm:chokidar@^4.0.3", - "dedent": "npm:dedent@^1.6.0", - "fast-csv": "npm:fast-csv@^5.0.5", - "jsondiffpatch": "npm:jsondiffpatch@^0.7.3", - "nunjucks": "npm:nunjucks@^3.2.4", - "pgsql-deparser": "npm:pgsql-deparser@^17.11.1", - "prettier": "npm:prettier@^3", - "prettier-plugin-sql": "npm:prettier-plugin-sql@^0.19", - "sql-highlight": "npm:sql-highlight@^6.1.0", - "postgresjs": "https://deno.land/x/postgresjs@v3.4.3/mod.js", - "zod": "npm:zod@^4.1.12" - } -} diff --git a/deno.lock b/deno.lock deleted file mode 100644 index f770d27..0000000 --- a/deno.lock +++ /dev/null @@ -1,1774 +0,0 @@ -{ - "version": "5", - "specifiers": { - "jsr:@opentelemetry/api@^1.9.0": "1.9.0", - "jsr:@rabbit-company/rate-limiter@3": "3.0.0", - "jsr:@std/assert@^1.0.13": "1.0.14", - "jsr:@std/assert@^1.0.14": "1.0.14", - "jsr:@std/assert@^1.0.15": "1.0.16", - "jsr:@std/async@^1.0.15": "1.0.15", - "jsr:@std/collections@^1.1.3": "1.1.3", - "jsr:@std/data-structures@^1.0.9": "1.0.9", - "jsr:@std/fmt@^1.0.8": "1.0.8", - "jsr:@std/fs@^1.0.19": "1.0.20", - "jsr:@std/internal@^1.0.10": "1.0.10", - "jsr:@std/internal@^1.0.12": "1.0.12", - "jsr:@std/path@^1.1.2": "1.1.3", - "jsr:@std/path@^1.1.3": "1.1.3", - "jsr:@std/testing@^1.0.16": "1.0.16", - "npm:@actions/core@^1.11.1": "1.11.1", - "npm:@actions/github@^6.0.1": "6.0.1_@octokit+core@5.2.2", - "npm:@libpg-query/parser@^17.6.3": "17.6.3", - "npm:@pgsql/types@^17.6.1": "17.6.2", - "npm:@query-doctor/core@0.4": "0.4.0", - "npm:@testcontainers/postgresql@^11.9.0": "11.9.0", - "npm:@types/node@^24.9.1": "24.10.1", - "npm:@types/nunjucks@^3.2.6": "3.2.6", - "npm:async-sema@^3.1.1": "3.1.1", - "npm:chokidar@^4.0.3": "4.0.3", - "npm:dedent@^1.6.0": "1.7.1", - "npm:fast-csv@^5.0.5": "5.0.5", - "npm:jsondiffpatch@~0.7.3": "0.7.3", - "npm:nunjucks@^3.2.4": "3.2.4_chokidar@4.0.3", - "npm:pgsql-deparser@^17.11.1": "17.17.2", - "npm:prettier-plugin-sql@0.19": "0.19.2_prettier@3.7.4", - "npm:prettier@3": "3.7.4", - "npm:sql-highlight@^6.1.0": "6.1.0", - "npm:zod@^4.1.12": "4.1.13" - }, - "jsr": { - "@opentelemetry/api@1.9.0": { - "integrity": "a8a4980ff5eff7bd322715cad1a128b3208675f1e05b8adbfcf6b240502739b7" - }, - "@rabbit-company/rate-limiter@3.0.0": { - "integrity": "7cb13dea09da7cfa93476ded4ee8acd893689400ddd955219c0f3f8f911cff52" - }, - "@std/assert@1.0.14": { - "integrity": "68d0d4a43b365abc927f45a9b85c639ea18a9fab96ad92281e493e4ed84abaa4", - "dependencies": [ - "jsr:@std/internal@^1.0.10" - ] - }, - "@std/assert@1.0.16": { - "integrity": "6a7272ed1eaa77defe76e5ff63ca705d9c495077e2d5fd0126d2b53fc5bd6532", - "dependencies": [ - "jsr:@std/internal@^1.0.12" - ] - }, - "@std/async@1.0.15": { - "integrity": "55d1d9d04f99403fe5730ab16bdcc3c47f658a6bf054cafb38a50f046238116e" - }, - "@std/collections@1.1.3": { - "integrity": "bf8b0818886df6a32b64c7d3b037a425111f28278d69fd0995aeb62777c986b0" - }, - "@std/data-structures@1.0.9": { - "integrity": "033d6e17e64bf1f84a614e647c1b015fa2576ae3312305821e1a4cb20674bb4d", - "dependencies": [ - "jsr:@std/assert@^1.0.13" - ] - }, - "@std/fmt@1.0.8": { - "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" - }, - "@std/fs@1.0.20": { - "integrity": "e953206aae48d46ee65e8783ded459f23bec7dd1f3879512911c35e5484ea187", - "dependencies": [ - "jsr:@std/path@^1.1.3" - ] - }, - "@std/internal@1.0.10": { - "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7" - }, - "@std/internal@1.0.12": { - "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" - }, - "@std/path@1.1.3": { - "integrity": "b015962d82a5e6daea980c32b82d2c40142149639968549c649031a230b1afb3", - "dependencies": [ - "jsr:@std/internal@^1.0.12" - ] - }, - "@std/testing@1.0.16": { - "integrity": "a917ffdeb5924c9be436dc78bc32e511760e14d3a96e49c607fc5ecca86d0092", - "dependencies": [ - "jsr:@std/assert@^1.0.15", - "jsr:@std/async", - "jsr:@std/data-structures", - "jsr:@std/fs", - "jsr:@std/internal@^1.0.12", - "jsr:@std/path@^1.1.2" - ] - } - }, - "npm": { - "@actions/core@1.11.1": { - "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", - "dependencies": [ - "@actions/exec", - "@actions/http-client" - ] - }, - "@actions/exec@1.1.1": { - "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", - "dependencies": [ - "@actions/io" - ] - }, - "@actions/github@6.0.1_@octokit+core@5.2.2": { - "integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==", - "dependencies": [ - "@actions/http-client", - "@octokit/core", - "@octokit/plugin-paginate-rest", - "@octokit/plugin-rest-endpoint-methods", - "@octokit/request", - "@octokit/request-error", - "undici@5.29.0" - ] - }, - "@actions/http-client@2.2.3": { - "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", - "dependencies": [ - "tunnel", - "undici@5.29.0" - ] - }, - "@actions/io@1.1.3": { - "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" - }, - "@balena/dockerignore@1.0.2": { - "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==" - }, - "@dmsnell/diff-match-patch@1.1.0": { - "integrity": "sha512-yejLPmM5pjsGvxS9gXablUSbInW7H976c/FJ4iQxWIm7/38xBySRemTPDe34lhg1gVLbJntX0+sH0jYfU+PN9A==" - }, - "@fast-csv/format@5.0.5": { - "integrity": "sha512-0P9SJXXnqKdmuWlLaTelqbrfdgN37Mvrb369J6eNmqL41IEIZQmV4sNM4GgAK2Dz3aH04J0HKGDMJFkYObThTw==", - "dependencies": [ - "lodash.escaperegexp", - "lodash.isboolean", - "lodash.isfunction", - "lodash.isnil" - ] - }, - "@fast-csv/parse@5.0.5": { - "integrity": "sha512-M0IbaXZDbxfOnpVE5Kps/a6FGlILLhtLsvWd9qNH3d2TxNnpbNkFf3KD26OmJX6MHq7PdQAl5htStDwnuwHx6w==", - "dependencies": [ - "lodash.escaperegexp", - "lodash.groupby", - "lodash.isfunction", - "lodash.isnil", - "lodash.isundefined", - "lodash.uniq" - ] - }, - "@fastify/busboy@2.1.1": { - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==" - }, - "@grpc/grpc-js@1.14.2": { - "integrity": "sha512-QzVUtEFyu05UNx2xr0fCQmStUO17uVQhGNowtxs00IgTZT6/W2PBLfUkj30s0FKJ29VtTa3ArVNIhNP6akQhqA==", - "dependencies": [ - "@grpc/proto-loader@0.8.0", - "@js-sdsl/ordered-map" - ] - }, - "@grpc/proto-loader@0.7.15": { - "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", - "dependencies": [ - "lodash.camelcase", - "long", - "protobufjs", - "yargs" - ], - "bin": true - }, - "@grpc/proto-loader@0.8.0": { - "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", - "dependencies": [ - "lodash.camelcase", - "long", - "protobufjs", - "yargs" - ], - "bin": true - }, - "@isaacs/cliui@8.0.2": { - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dependencies": [ - "string-width@5.1.2", - "string-width-cjs@npm:string-width@4.2.3", - "strip-ansi@7.1.2", - "strip-ansi-cjs@npm:strip-ansi@6.0.1", - "wrap-ansi@8.1.0", - "wrap-ansi-cjs@npm:wrap-ansi@7.0.0" - ] - }, - "@js-sdsl/ordered-map@4.4.2": { - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==" - }, - "@launchql/protobufjs@7.2.6": { - "integrity": "sha512-vwi1nG2/heVFsIMHQU1KxTjUp5c757CTtRAZn/jutApCkFlle1iv8tzM/DHlSZJKDldxaYqnNYTg0pTyp8Bbtg==", - "dependencies": [ - "@protobufjs/aspromise", - "@protobufjs/base64", - "@protobufjs/codegen", - "@protobufjs/eventemitter", - "@protobufjs/fetch", - "@protobufjs/float", - "@protobufjs/inquire", - "@protobufjs/path", - "@protobufjs/pool", - "@protobufjs/utf8", - "@types/node", - "long" - ], - "scripts": true - }, - "@libpg-query/parser@17.6.3": { - "integrity": "sha512-AvbS7b9GJZfCzqt4tLMqTaYK7Css9pJRTA2dKWLoTlob/XO2VNc30Q3g9DmxNBmokVTrmibkn/dy9bw8hfosQQ==", - "dependencies": [ - "@launchql/protobufjs", - "@pgsql/types" - ] - }, - "@octokit/auth-token@4.0.0": { - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==" - }, - "@octokit/core@5.2.2": { - "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", - "dependencies": [ - "@octokit/auth-token", - "@octokit/graphql", - "@octokit/request", - "@octokit/request-error", - "@octokit/types@13.10.0", - "before-after-hook", - "universal-user-agent" - ] - }, - "@octokit/endpoint@9.0.6": { - "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", - "dependencies": [ - "@octokit/types@13.10.0", - "universal-user-agent" - ] - }, - "@octokit/graphql@7.1.1": { - "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", - "dependencies": [ - "@octokit/request", - "@octokit/types@13.10.0", - "universal-user-agent" - ] - }, - "@octokit/openapi-types@20.0.0": { - "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" - }, - "@octokit/openapi-types@24.2.0": { - "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==" - }, - "@octokit/plugin-paginate-rest@9.2.2_@octokit+core@5.2.2": { - "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", - "dependencies": [ - "@octokit/core", - "@octokit/types@12.6.0" - ] - }, - "@octokit/plugin-rest-endpoint-methods@10.4.1_@octokit+core@5.2.2": { - "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", - "dependencies": [ - "@octokit/core", - "@octokit/types@12.6.0" - ] - }, - "@octokit/request-error@5.1.1": { - "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", - "dependencies": [ - "@octokit/types@13.10.0", - "deprecation", - "once" - ] - }, - "@octokit/request@8.4.1": { - "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", - "dependencies": [ - "@octokit/endpoint", - "@octokit/request-error", - "@octokit/types@13.10.0", - "universal-user-agent" - ] - }, - "@octokit/types@12.6.0": { - "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", - "dependencies": [ - "@octokit/openapi-types@20.0.0" - ] - }, - "@octokit/types@13.10.0": { - "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", - "dependencies": [ - "@octokit/openapi-types@24.2.0" - ] - }, - "@pgsql/types@17.6.2": { - "integrity": "sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==" - }, - "@pkgjs/parseargs@0.11.0": { - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==" - }, - "@protobufjs/aspromise@1.1.2": { - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64@1.1.2": { - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen@2.0.4": { - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter@1.1.0": { - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch@1.1.0": { - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": [ - "@protobufjs/aspromise", - "@protobufjs/inquire" - ] - }, - "@protobufjs/float@1.0.2": { - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire@1.1.0": { - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path@1.1.2": { - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool@1.1.0": { - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8@1.1.0": { - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@query-doctor/core@0.4.0": { - "integrity": "sha512-d04ea11S9/hCkhXABFIbcNHP0uBzZ5WjFnCmYmjl3Yzl9XNm4KosJjI0jngnOQKuwSwNii0vvdYMxuOfMUiJNw==", - "dependencies": [ - "@pgsql/types", - "colorette", - "dedent", - "pgsql-deparser", - "zod" - ] - }, - "@testcontainers/postgresql@11.9.0": { - "integrity": "sha512-beLyLdLygFllktviM132Xd6tQ4i5FnuyZP+4BQEjUb5sJYHYnIrV/ZBzRRflIlF8gugt1GXgudkmr/HxM9vtKw==", - "dependencies": [ - "testcontainers" - ] - }, - "@types/docker-modem@3.0.6": { - "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", - "dependencies": [ - "@types/node", - "@types/ssh2" - ] - }, - "@types/dockerode@3.3.47": { - "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", - "dependencies": [ - "@types/docker-modem", - "@types/node", - "@types/ssh2" - ] - }, - "@types/node@24.10.1": { - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dependencies": [ - "undici-types" - ] - }, - "@types/nunjucks@3.2.6": { - "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==" - }, - "@types/pegjs@0.10.6": { - "integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==" - }, - "@types/ssh2-streams@0.1.13": { - "integrity": "sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==", - "dependencies": [ - "@types/node" - ] - }, - "@types/ssh2@0.5.52": { - "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", - "dependencies": [ - "@types/node", - "@types/ssh2-streams" - ] - }, - "a-sync-waterfall@1.0.1": { - "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" - }, - "abort-controller@3.0.0": { - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": [ - "event-target-shim" - ] - }, - "ansi-regex@5.0.1": { - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-regex@6.2.2": { - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==" - }, - "ansi-styles@4.3.0": { - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": [ - "color-convert" - ] - }, - "ansi-styles@6.2.3": { - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==" - }, - "archiver-utils@5.0.2": { - "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", - "dependencies": [ - "glob", - "graceful-fs", - "is-stream", - "lazystream", - "lodash", - "normalize-path", - "readable-stream@4.7.0" - ] - }, - "archiver@7.0.1": { - "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", - "dependencies": [ - "archiver-utils", - "async", - "buffer-crc32", - "readable-stream@4.7.0", - "readdir-glob", - "tar-stream@3.1.7", - "zip-stream" - ] - }, - "argparse@2.0.1": { - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "asap@2.0.6": { - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "asn1@0.2.6": { - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dependencies": [ - "safer-buffer" - ] - }, - "async-lock@1.4.1": { - "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" - }, - "async-sema@3.1.1": { - "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==" - }, - "async@3.2.6": { - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" - }, - "b4a@1.7.3": { - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==" - }, - "balanced-match@1.0.2": { - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "bare-events@2.8.2": { - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==" - }, - "bare-fs@4.5.2_bare-events@2.8.2": { - "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", - "dependencies": [ - "bare-events", - "bare-path", - "bare-stream", - "bare-url", - "fast-fifo" - ] - }, - "bare-os@3.6.2": { - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==" - }, - "bare-path@3.0.0": { - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "dependencies": [ - "bare-os" - ] - }, - "bare-stream@2.7.0_bare-events@2.8.2": { - "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", - "dependencies": [ - "bare-events", - "streamx" - ], - "optionalPeers": [ - "bare-events" - ] - }, - "bare-url@2.3.2": { - "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", - "dependencies": [ - "bare-path" - ] - }, - "base64-js@1.5.1": { - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bcrypt-pbkdf@1.0.2": { - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": [ - "tweetnacl" - ] - }, - "before-after-hook@2.2.3": { - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "big-integer@1.6.52": { - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==" - }, - "bl@4.1.0": { - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": [ - "buffer@5.7.1", - "inherits", - "readable-stream@3.6.2" - ] - }, - "brace-expansion@2.0.2": { - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dependencies": [ - "balanced-match" - ] - }, - "buffer-crc32@1.0.0": { - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==" - }, - "buffer@5.7.1": { - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dependencies": [ - "base64-js", - "ieee754" - ] - }, - "buffer@6.0.3": { - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dependencies": [ - "base64-js", - "ieee754" - ] - }, - "buildcheck@0.0.7": { - "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==" - }, - "byline@5.0.0": { - "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==" - }, - "chokidar@4.0.3": { - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dependencies": [ - "readdirp" - ] - }, - "chownr@1.1.4": { - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "cliui@8.0.1": { - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": [ - "string-width@4.2.3", - "strip-ansi@6.0.1", - "wrap-ansi@7.0.0" - ] - }, - "color-convert@2.0.1": { - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": [ - "color-name" - ] - }, - "color-name@1.1.4": { - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "colorette@2.0.20": { - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "commander@2.20.3": { - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "commander@5.1.0": { - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" - }, - "compress-commons@6.0.2": { - "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", - "dependencies": [ - "crc-32", - "crc32-stream", - "is-stream", - "normalize-path", - "readable-stream@4.7.0" - ] - }, - "core-util-is@1.0.3": { - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "cpu-features@0.0.10": { - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "dependencies": [ - "buildcheck", - "nan" - ], - "scripts": true - }, - "crc-32@1.2.2": { - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "bin": true - }, - "crc32-stream@6.0.0": { - "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", - "dependencies": [ - "crc-32", - "readable-stream@4.7.0" - ] - }, - "cross-spawn@7.0.6": { - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dependencies": [ - "path-key", - "shebang-command", - "which" - ] - }, - "debug@4.4.3": { - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dependencies": [ - "ms" - ] - }, - "dedent@1.7.1": { - "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==" - }, - "deprecation@2.3.1": { - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "discontinuous-range@1.0.0": { - "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==" - }, - "docker-compose@1.3.0": { - "integrity": "sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==", - "dependencies": [ - "yaml" - ] - }, - "docker-modem@5.0.6": { - "integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==", - "dependencies": [ - "debug", - "readable-stream@3.6.2", - "split-ca", - "ssh2" - ] - }, - "dockerode@4.0.9": { - "integrity": "sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q==", - "dependencies": [ - "@balena/dockerignore", - "@grpc/grpc-js", - "@grpc/proto-loader@0.7.15", - "docker-modem", - "protobufjs", - "tar-fs@2.1.4", - "uuid" - ] - }, - "eastasianwidth@0.2.0": { - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "emoji-regex@8.0.0": { - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "emoji-regex@9.2.2": { - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "end-of-stream@1.4.5": { - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dependencies": [ - "once" - ] - }, - "escalade@3.2.0": { - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" - }, - "event-target-shim@5.0.1": { - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "events-universal@1.0.1": { - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "dependencies": [ - "bare-events" - ] - }, - "events@3.3.0": { - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "fast-csv@5.0.5": { - "integrity": "sha512-9//QpogDIPln5Dc8e3Q3vbSSLXlTeU7z1JqsUOXZYOln8EIn/OOO8+NS2c3ukR6oYngDd3+P1HXSkby3kNV9KA==", - "dependencies": [ - "@fast-csv/format", - "@fast-csv/parse" - ] - }, - "fast-fifo@1.3.2": { - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" - }, - "foreground-child@3.3.1": { - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dependencies": [ - "cross-spawn", - "signal-exit@4.1.0" - ] - }, - "fs-constants@1.0.0": { - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "get-caller-file@2.0.5": { - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-port@7.1.0": { - "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==" - }, - "glob@10.5.0": { - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dependencies": [ - "foreground-child", - "jackspeak", - "minimatch@9.0.5", - "minipass", - "package-json-from-dist", - "path-scurry" - ], - "deprecated": true, - "bin": true - }, - "graceful-fs@4.2.11": { - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "ieee754@1.2.1": { - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "inherits@2.0.4": { - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-fullwidth-code-point@3.0.0": { - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-stream@2.0.1": { - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "isarray@1.0.0": { - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "isexe@2.0.0": { - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "jackspeak@3.4.3": { - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dependencies": [ - "@isaacs/cliui" - ], - "optionalDependencies": [ - "@pkgjs/parseargs" - ] - }, - "jsondiffpatch@0.7.3": { - "integrity": "sha512-zd4dqFiXSYyant2WgSXAZ9+yYqilNVvragVNkNRn2IFZKgjyULNrKRznqN4Zon0MkLueCg+3QaPVCnDAVP20OQ==", - "dependencies": [ - "@dmsnell/diff-match-patch" - ], - "bin": true - }, - "jsox@1.2.125": { - "integrity": "sha512-HIf1uwublnXZsy7p3yHTrhzMzrLO6xKnqXytT9pEil5QxaXi8eyer7Is4luF5hYSV4kD3v03Y32FWoAeVYTghQ==", - "bin": true - }, - "lazystream@1.0.1": { - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dependencies": [ - "readable-stream@2.3.8" - ] - }, - "lodash.camelcase@4.3.0": { - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "lodash.escaperegexp@4.1.2": { - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" - }, - "lodash.groupby@4.6.0": { - "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" - }, - "lodash.isboolean@3.0.3": { - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "lodash.isfunction@3.0.9": { - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" - }, - "lodash.isnil@4.0.0": { - "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" - }, - "lodash.isundefined@3.0.1": { - "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" - }, - "lodash.uniq@4.5.0": { - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "lodash@4.17.21": { - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "long@5.3.2": { - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" - }, - "lru-cache@10.4.3": { - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" - }, - "minimatch@5.1.6": { - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": [ - "brace-expansion" - ] - }, - "minimatch@9.0.5": { - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dependencies": [ - "brace-expansion" - ] - }, - "minipass@7.1.2": { - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" - }, - "mkdirp-classic@0.5.3": { - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, - "mkdirp@1.0.4": { - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": true - }, - "moo@0.5.2": { - "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" - }, - "ms@2.1.3": { - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "nan@2.24.0": { - "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==" - }, - "nearley@2.20.1": { - "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", - "dependencies": [ - "commander@2.20.3", - "moo", - "railroad-diagrams", - "randexp" - ], - "bin": true - }, - "node-sql-parser@5.3.13": { - "integrity": "sha512-heyWv3lLjKHpcBDMUSR+R0DohRYZTYq+Ro3hJ4m9Ia8ccdKbL5UijIaWr2L4co+bmmFuvBVZ4v23QW2PqvBFAA==", - "dependencies": [ - "@types/pegjs", - "big-integer" - ] - }, - "normalize-path@3.0.0": { - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "nunjucks@3.2.4_chokidar@4.0.3": { - "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", - "dependencies": [ - "a-sync-waterfall", - "asap", - "chokidar", - "commander@5.1.0" - ], - "optionalPeers": [ - "chokidar" - ], - "bin": true - }, - "once@1.4.0": { - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": [ - "wrappy" - ] - }, - "package-json-from-dist@1.0.1": { - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" - }, - "path-key@3.1.1": { - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-scurry@1.11.1": { - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dependencies": [ - "lru-cache", - "minipass" - ] - }, - "pgsql-deparser@17.17.2": { - "integrity": "sha512-FCjqKY3Sdmce3VUd3CxCXF0kqaZ0s4a6yIMT5UJ9vETh0cF54A8Tpqjn0qBKaPUD8xqTKeLdS+SfiwjAC64wrA==", - "dependencies": [ - "@pgsql/types" - ] - }, - "prettier-plugin-sql@0.19.2_prettier@3.7.4": { - "integrity": "sha512-DAu1Jcanpvs32OAOXsqaVXOpPs4nFLVkB3XwzRiZZVNL5/c+XdlNxWFMiMpMhYhmCG5BW3srK8mhikCOv5tPfg==", - "dependencies": [ - "jsox", - "node-sql-parser", - "prettier", - "sql-formatter", - "tslib" - ] - }, - "prettier@3.7.4": { - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", - "bin": true - }, - "process-nextick-args@2.0.1": { - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "process@0.11.10": { - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" - }, - "proper-lockfile@4.1.2": { - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dependencies": [ - "graceful-fs", - "retry", - "signal-exit@3.0.7" - ] - }, - "properties-reader@2.3.0": { - "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", - "dependencies": [ - "mkdirp" - ] - }, - "protobufjs@7.5.4": { - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", - "dependencies": [ - "@protobufjs/aspromise", - "@protobufjs/base64", - "@protobufjs/codegen", - "@protobufjs/eventemitter", - "@protobufjs/fetch", - "@protobufjs/float", - "@protobufjs/inquire", - "@protobufjs/path", - "@protobufjs/pool", - "@protobufjs/utf8", - "@types/node", - "long" - ], - "scripts": true - }, - "pump@3.0.3": { - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dependencies": [ - "end-of-stream", - "once" - ] - }, - "railroad-diagrams@1.0.0": { - "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==" - }, - "randexp@0.4.6": { - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "dependencies": [ - "discontinuous-range", - "ret" - ] - }, - "readable-stream@2.3.8": { - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": [ - "core-util-is", - "inherits", - "isarray", - "process-nextick-args", - "safe-buffer@5.1.2", - "string_decoder@1.1.1", - "util-deprecate" - ] - }, - "readable-stream@3.6.2": { - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": [ - "inherits", - "string_decoder@1.3.0", - "util-deprecate" - ] - }, - "readable-stream@4.7.0": { - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dependencies": [ - "abort-controller", - "buffer@6.0.3", - "events", - "process", - "string_decoder@1.3.0" - ] - }, - "readdir-glob@1.1.3": { - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "dependencies": [ - "minimatch@5.1.6" - ] - }, - "readdirp@4.1.2": { - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==" - }, - "require-directory@2.1.1": { - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" - }, - "ret@0.1.15": { - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "retry@0.12.0": { - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==" - }, - "safe-buffer@5.1.2": { - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-buffer@5.2.1": { - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer@2.1.2": { - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "shebang-command@2.0.0": { - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": [ - "shebang-regex" - ] - }, - "shebang-regex@3.0.0": { - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit@3.0.7": { - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "signal-exit@4.1.0": { - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" - }, - "split-ca@1.0.1": { - "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" - }, - "sql-formatter@15.6.12": { - "integrity": "sha512-mkpF+RG402P66VMsnQkWewTRzDBWfu9iLbOfxaW/nAKOS/2A9MheQmcU5cmX0D0At9azrorZwpvcBRNNBozACQ==", - "dependencies": [ - "argparse", - "nearley" - ], - "bin": true - }, - "sql-highlight@6.1.0": { - "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==" - }, - "ssh-remote-port-forward@1.0.4": { - "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", - "dependencies": [ - "@types/ssh2", - "ssh2" - ] - }, - "ssh2@1.17.0": { - "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", - "dependencies": [ - "asn1", - "bcrypt-pbkdf" - ], - "optionalDependencies": [ - "cpu-features", - "nan" - ], - "scripts": true - }, - "streamx@2.23.0": { - "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", - "dependencies": [ - "events-universal", - "fast-fifo", - "text-decoder" - ] - }, - "string-width@4.2.3": { - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": [ - "emoji-regex@8.0.0", - "is-fullwidth-code-point", - "strip-ansi@6.0.1" - ] - }, - "string-width@5.1.2": { - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": [ - "eastasianwidth", - "emoji-regex@9.2.2", - "strip-ansi@7.1.2" - ] - }, - "string_decoder@1.1.1": { - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": [ - "safe-buffer@5.1.2" - ] - }, - "string_decoder@1.3.0": { - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": [ - "safe-buffer@5.2.1" - ] - }, - "strip-ansi@6.0.1": { - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": [ - "ansi-regex@5.0.1" - ] - }, - "strip-ansi@7.1.2": { - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dependencies": [ - "ansi-regex@6.2.2" - ] - }, - "tar-fs@2.1.4": { - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "dependencies": [ - "chownr", - "mkdirp-classic", - "pump", - "tar-stream@2.2.0" - ] - }, - "tar-fs@3.1.1": { - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", - "dependencies": [ - "pump", - "tar-stream@3.1.7" - ], - "optionalDependencies": [ - "bare-fs", - "bare-path" - ] - }, - "tar-stream@2.2.0": { - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": [ - "bl", - "end-of-stream", - "fs-constants", - "inherits", - "readable-stream@3.6.2" - ] - }, - "tar-stream@3.1.7": { - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dependencies": [ - "b4a", - "fast-fifo", - "streamx" - ] - }, - "testcontainers@11.9.0": { - "integrity": "sha512-SQ6OqQUig7HcGVF72i+ZVIMvxPSpEz8cgC/B63ekqMzgf98DnveoBbOmqux/Wa5wQAQCt4mEPNMa/Jz7vMg9fQ==", - "dependencies": [ - "@balena/dockerignore", - "@types/dockerode", - "archiver", - "async-lock", - "byline", - "debug", - "docker-compose", - "dockerode", - "get-port", - "proper-lockfile", - "properties-reader", - "ssh-remote-port-forward", - "tar-fs@3.1.1", - "tmp", - "undici@7.16.0" - ] - }, - "text-decoder@1.2.3": { - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "dependencies": [ - "b4a" - ] - }, - "tmp@0.2.5": { - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==" - }, - "tslib@2.8.1": { - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "tunnel@0.0.6": { - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, - "tweetnacl@0.14.5": { - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "undici-types@7.16.0": { - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" - }, - "undici@5.29.0": { - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", - "dependencies": [ - "@fastify/busboy" - ] - }, - "undici@7.16.0": { - "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==" - }, - "universal-user-agent@6.0.1": { - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" - }, - "util-deprecate@1.0.2": { - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "uuid@10.0.0": { - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "bin": true - }, - "which@2.0.2": { - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": [ - "isexe" - ], - "bin": true - }, - "wrap-ansi@7.0.0": { - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": [ - "ansi-styles@4.3.0", - "string-width@4.2.3", - "strip-ansi@6.0.1" - ] - }, - "wrap-ansi@8.1.0": { - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": [ - "ansi-styles@6.2.3", - "string-width@5.1.2", - "strip-ansi@7.1.2" - ] - }, - "wrappy@1.0.2": { - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "y18n@5.0.8": { - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yaml@2.8.2": { - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "bin": true - }, - "yargs-parser@21.1.1": { - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" - }, - "yargs@17.7.2": { - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": [ - "cliui", - "escalade", - "get-caller-file", - "require-directory", - "string-width@4.2.3", - "y18n", - "yargs-parser" - ] - }, - "zip-stream@6.0.1": { - "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", - "dependencies": [ - "archiver-utils", - "compress-commons", - "readable-stream@4.7.0" - ] - }, - "zod@4.1.13": { - "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==" - } - }, - "redirects": { - "https://deno.land/std/path/mod.ts": "https://deno.land/std@0.224.0/path/mod.ts" - }, - "remote": { - "https://deno.land/std@0.132.0/_deno_unstable.ts": "23a1a36928f1b6d3b0170aaa67de09af12aa998525f608ff7331b9fb364cbde6", - "https://deno.land/std@0.132.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", - "https://deno.land/std@0.132.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", - "https://deno.land/std@0.132.0/_wasm_crypto/crypto.mjs": "3b383eb715e8bfe61b4450ef0644b2653429c88d494807c86c5235979f62e56b", - "https://deno.land/std@0.132.0/_wasm_crypto/crypto.wasm.mjs": "0ad9ecc0d03ca8a083d9109db22e7507f019f63cf55b82ea618ab58855617577", - "https://deno.land/std@0.132.0/_wasm_crypto/mod.ts": "30a93c8b6b6c5b269e96a3e95d2c045d86a496814a8737443b77cad941d6a0b5", - "https://deno.land/std@0.132.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06", - "https://deno.land/std@0.132.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0", - "https://deno.land/std@0.132.0/async/debounce.ts": "564273ef242bcfcda19a439132f940db8694173abffc159ea34f07d18fc42620", - "https://deno.land/std@0.132.0/async/deferred.ts": "bc18e28108252c9f67dfca2bbc4587c3cbf3aeb6e155f8c864ca8ecff992b98a", - "https://deno.land/std@0.132.0/async/delay.ts": "cbbdf1c87d1aed8edc7bae13592fb3e27e3106e0748f089c263390d4f49e5f6c", - "https://deno.land/std@0.132.0/async/mod.ts": "2240c6841157738414331f47dee09bb8c0482c5b1980b6e3234dd03515c8132f", - "https://deno.land/std@0.132.0/async/mux_async_iterator.ts": "f4d1d259b0c694d381770ddaaa4b799a94843eba80c17f4a2ec2949168e52d1e", - "https://deno.land/std@0.132.0/async/pool.ts": "97b0dd27c69544e374df857a40902e74e39532f226005543eabacb551e277082", - "https://deno.land/std@0.132.0/async/tee.ts": "1341feb1f5b1a96f8628d0f8fc07d8c43d3813423f18a63bf1b4785568d21b1f", - "https://deno.land/std@0.132.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", - "https://deno.land/std@0.132.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", - "https://deno.land/std@0.132.0/bytes/mod.ts": "d3b455c0dbd4804644159d1e25946ade5ee385d2359894de49e2c6101b18b7a9", - "https://deno.land/std@0.132.0/encoding/base64.ts": "c8c16b4adaa60d7a8eee047c73ece26844435e8f7f1328d74593dbb2dd58ea4f", - "https://deno.land/std@0.132.0/encoding/base64url.ts": "55f9d13df02efac10c6f96169daa3e702606a64e8aa27c0295f645f198c27130", - "https://deno.land/std@0.132.0/encoding/hex.ts": "7f023e1e51cfd6b189682e602e8640939e7be71a300a2fcf3daf8f84dc609bbc", - "https://deno.land/std@0.132.0/flags/mod.ts": "430cf2d1c26e00286373b2647ebdca637f7558505e88e9c108a4742cd184c916", - "https://deno.land/std@0.132.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", - "https://deno.land/std@0.132.0/fmt/printf.ts": "e2c0f72146aed1efecf0c39ab928b26ae493a2278f670a871a0fbdcf36ff3379", - "https://deno.land/std@0.132.0/fs/eol.ts": "b92f0b88036de507e7e6fbedbe8f666835ea9dcbf5ac85917fa1fadc919f83a5", - "https://deno.land/std@0.132.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d", - "https://deno.land/std@0.132.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8", - "https://deno.land/std@0.132.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", - "https://deno.land/std@0.132.0/node/_buffer.mjs": "f4a7df481d4eed06dc0151b833177d8ef74fc3a96dd4d2b073e690b6ced9474d", - "https://deno.land/std@0.132.0/node/_core.ts": "568d277be2e086af996cbdd599fec569f5280e9a494335ca23ad392b130d7bb9", - "https://deno.land/std@0.132.0/node/_crypto/constants.ts": "49011c87be4e45407ef5e99e96bde3f08656ebd8e6dfc99048c703dd0ce53952", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/base/buffer.js": "73beb8294eb29bd61458bbaaeeb51dfad4ec9c9868a62207a061d908f1637261", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/base/node.js": "4b777980d2a23088698fd2ff065bb311a2c713497d359e674cb6ef6baf267a0f", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/base/reporter.js": "8e4886e8ae311c9a92caf58bbbd8670326ceeae97430f4884e558e4acf8e8598", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/constants/der.js": "354b255479bff22a31d25bf08b217a295071700e37d0991cc05cac9f95e5e7ca", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/decoders/der.js": "c6faf66761daa43fbf79221308443893587c317774047b508a04c570713b76fb", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/decoders/pem.js": "8316ef7ce2ce478bc3dc1e9df1b75225d1eb8fb5d1378f8adf0cf19ecea5b501", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/encoders/der.js": "408336c88d17c5605ea64081261cf42267d8f9fda90098cb560aa6635bb00877", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/encoders/pem.js": "42a00c925b68c0858d6de0ba41ab89935b39fae9117bbf72a9abb2f4b755a2e7", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/mod.js": "7b78859707be10a0a1e4faccdd28cd5a4f71ad74a3e7bebda030757da97cd232", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/bn.js/bn.js": "abd1badd659fd0ae54e6a421a573a25aef4e795edc392178360cf716b144286d", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/aes.js": "1cf4c354c5bb341ffc9ab7207f471229835b021947225bce2e1642f26643847a", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/auth_cipher.js": "19b4dbb903e8406eb733176e6318d5e1a3bd382b67b72f7cf8e1c46cc6321ba4", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/decrypter.js": "05c1676942fd8e95837115bc2d1371bcf62e9bf19f6c3348870961fc64ddad0b", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/encrypter.js": "93ec98ab26fbeb5969eae2943e42fb66780f377b9b0ff0ecc32a9ed11201b142", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/ghash.js": "667b64845764a84f0096ef8cf7debed1a5f15ac9af26b379848237be57da399a", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/incr32.js": "4a7f0107753e4390b4ccc4dbd5200c5527d43f894f768e131903df30a09dfd67", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/mod.js": "d8eb88e7a317467831473621f32e60d7db9d981f6a2ae45d2fb2af170eab2d22", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cbc.js": "9790799cff181a074686c885708cb8eb473aeb3c86ff2e8d0ff911ae6c1e4431", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb.js": "a4e36ede6f26d8559d8f0528a134592761c706145a641bd9ad1100763e831cdb", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb1.js": "c6372f4973a68ca742682e81d1165e8869aaabf0091a8b963d4d60e5ee8e6f6a", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb8.js": "bd29eebb89199b056ff2441f7fb5e0300f458e13dcaaddbb8bc00cbdb199db67", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/ctr.js": "9c2cbac1fc8f9b58334faacb98e6c57e8c3712f673ea4cf2d528a2894998ab2f", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/ecb.js": "9629d193433688f0cfc432eca52838db0fb28d9eb4f45563df952bde50b59763", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/mod.js": "7d8516ef8a20565539eb17cad5bb70add02ac06d1891e8f47cb981c22821787e", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/ofb.js": "c23abaa6f1ec5343e9d7ba61d702acb3d81a0bd3d34dd2004e36975dc043d6ff", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/stream_cipher.js": "a533a03a2214c6b5934ce85a59eb1e04239fd6f429017c7ca3c443ec7e07e68f", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/xor.ts": "4417711c026eb9a07475067cd31fa601e88c2d6ababd606d33d1e74da6fcfd09", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_rsa.js": "de8c98d2379a70d8c239b4886e2b3a11c7204eec39ae6b65d978d0d516ee6b08", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/cipher_base.js": "f565ad9daf3b3dd3b68381bed848da94fb093a9e4e5a48c92f47e26cc229df39", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/evp_bytes_to_key.ts": "8bd9fa445576b3e39586bdbef7c907f1dfda201bf22602d2ca1c6d760366311e", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/asn1.js": "4f33b0197ffbe9cff62e5bad266e6b40d55874ea653552bb32ed251ad091f70a", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/certificate.js": "aab306870830a81ad188db8fa8e037d7f5dd6c5abdabbd9739558245d1a12224", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/fix_proc.js": "af3052b76f441878e102ffcfc7420692e65777af765e96f786310ae1acf7f76a", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/mod.js": "e923a13b27089a99eeb578d2ffb9b4cfe8ce690918fec05d0024fa126f3e1ce3", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/mgf.js": "5b81dc1680829b564fc5a00b842fb9c88916e4639b4fa27fa8bb6b759d272371", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/mod.js": "eb8b64d7a58ee3823c1b642e799cc7ed1257d99f4d4aefa2b4796dd112ec094a", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/private_decrypt.js": "0050df879f7c1338132c45056835f64e32140e2a2d5d03c1366ccce64855f632", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/public_encrypt.js": "0132cb4fb8f72593278474884195b9c52b4e9ba33d8ddd22116d07a07f47005a", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/with_public.js": "7373dac9b53b8331ccf3521c854a131dcb304a2e4d34cd116649118f7919ed0c", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/xor.js": "900c6fc8b95e1861d796193c41988f5f70a09c7059e42887a243d0113ecaf0fd", - "https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/randombytes.ts": "f465cd8e114a3c110297e0143445b12125d729b25bada5bd88d5b30cf612d7dd", - "https://deno.land/std@0.132.0/node/_crypto/hash.ts": "6a84a079412d09ead27b900590f0bede9924bc7ce522b8b7d55183a2aaf63a68", - "https://deno.land/std@0.132.0/node/_crypto/pbkdf2.ts": "00af38578729b3060371dfee70dae502a5848b4cc4787c48f634195cab1ce89a", - "https://deno.land/std@0.132.0/node/_crypto/randomBytes.ts": "04e276bcbfa55b3502c7169deab3f2bf58bbc5e9727634f8a150eff734338e90", - "https://deno.land/std@0.132.0/node/_crypto/randomFill.ts": "019ff2a8330c3ede6e65af28c5a8e3dee9d404975749c8dadf6ba11ccc28528e", - "https://deno.land/std@0.132.0/node/_crypto/randomInt.ts": "2db981c2baf4ddac07b6da71f90677f4acf4dc2d93f351563fdd084d645b8413", - "https://deno.land/std@0.132.0/node/_crypto/scrypt.ts": "caf07a6b8afa6ac582f80f99ed7dc7fefd5476fcd2aad771b8440e5b883d6d70", - "https://deno.land/std@0.132.0/node/_crypto/timingSafeEqual.ts": "4a4ef17e482889d9d82138d5ffc0e787c32c04b1f12b28d076b1a69ceca46af1", - "https://deno.land/std@0.132.0/node/_crypto/types.ts": "d3fae758c5b62f63d8126c76eec31a5559a2f34305defb5fe2a7d9034057ff54", - "https://deno.land/std@0.132.0/node/_dns/_utils.ts": "42494c8b8fa1c13eb134c5696744f77197717fa857e4d05147f2395a739b0a40", - "https://deno.land/std@0.132.0/node/_events.mjs": "d7d56df4b9f69e445064bad5e5558978fb18c18c439bbb62fa13149b40d7fb99", - "https://deno.land/std@0.132.0/node/_fixed_queue.ts": "455b3c484de48e810b13bdf95cd1658ecb1ba6bcb8b9315ffe994efcde3ba5f5", - "https://deno.land/std@0.132.0/node/_fs/_fs_access.ts": "0700488320d37208d000b94767ab37208d469550767edab69b65b09a330f245d", - "https://deno.land/std@0.132.0/node/_fs/_fs_appendFile.ts": "5dca59d7f2ec33316d75da3d6a12d39f5c35b429bddf83f4b2c030b3a289d4b3", - "https://deno.land/std@0.132.0/node/_fs/_fs_chmod.ts": "8fc25677b82a2643686e6f8270a8f1bee87dd60986334591450699e199dac7d5", - "https://deno.land/std@0.132.0/node/_fs/_fs_chown.ts": "57858c54d376648fc3c8cf5a8ad4f7f19fb153b75fac3ed41df0332d757e7de9", - "https://deno.land/std@0.132.0/node/_fs/_fs_close.ts": "785a9d1a6d615e8aa9f5a4ac50c9a131931f8b0e17b3d4671cd1fd25a5c10f2b", - "https://deno.land/std@0.132.0/node/_fs/_fs_common.ts": "6a373d1583d9ec5cc7a8ff1072d77dc999e35282a320b7477038a2b209c304d3", - "https://deno.land/std@0.132.0/node/_fs/_fs_constants.ts": "5c20b190fc6b7cfdaf12a30ba545fc787db2c7bbe87ed5b890da99578116a339", - "https://deno.land/std@0.132.0/node/_fs/_fs_copy.ts": "675eb02a2dfc20dab1186bf6ed0a33b1abae656f440bc0a3ce74f385e0052eef", - "https://deno.land/std@0.132.0/node/_fs/_fs_dir.ts": "8a05f72e32dd568b41ef45f8f55f1f54e9a306a7588475fa7014289cd12872d9", - "https://deno.land/std@0.132.0/node/_fs/_fs_dirent.ts": "649c0a794e7b8d930cdd7e6a168b404aa0448bf784e0cfbe1bd6d25b99052273", - "https://deno.land/std@0.132.0/node/_fs/_fs_exists.ts": "83e9ca6ea1ab3c6c7c3fc45f3c1287ee88839f08140ac11056441537450055bb", - "https://deno.land/std@0.132.0/node/_fs/_fs_fdatasync.ts": "bbd078fea6c62c64d898101d697aefbfbb722797a75e328a82c2a4f2e7eb963d", - "https://deno.land/std@0.132.0/node/_fs/_fs_fstat.ts": "559ff6ff094337db37b0f3108aeaecf42672795af45b206adbd90105afebf9c6", - "https://deno.land/std@0.132.0/node/_fs/_fs_fsync.ts": "590be69ce5363dd4f8867f244cfabe8df89d40f86bbbe44fd00d69411d0b798e", - "https://deno.land/std@0.132.0/node/_fs/_fs_ftruncate.ts": "8eb2a9fcf026bd9b85dc07a22bc452c48db4be05ab83f5f2b6a0549e15c1f75f", - "https://deno.land/std@0.132.0/node/_fs/_fs_futimes.ts": "c753cb9e9f129a11d1110ed43905b8966ac2a1d362ed69d5a34bb44513b00082", - "https://deno.land/std@0.132.0/node/_fs/_fs_link.ts": "3f9ccce31c2e56284fbcf2c65ec2e6fed1d9e67a9997410223486ac5092888e3", - "https://deno.land/std@0.132.0/node/_fs/_fs_lstat.ts": "571cea559d270e3b2e7fc585b0eb051899f6d0e54b1786f5e2cee3e9f71e7f27", - "https://deno.land/std@0.132.0/node/_fs/_fs_mkdir.ts": "68421a23b6d3c2d0142a6d0b3ccdd87903f9c8f98d6754aba554ab4c6b435bb8", - "https://deno.land/std@0.132.0/node/_fs/_fs_mkdtemp.ts": "86eaec96c63ea178c749fa856115a345e9797baecad22297b9ef98e3d62b90e2", - "https://deno.land/std@0.132.0/node/_fs/_fs_open.ts": "b1ca72addd2723b2a5a876378e72609fbe168adad2006f5d7b4f1868beef65ca", - "https://deno.land/std@0.132.0/node/_fs/_fs_read.ts": "3b4ef96aad20f3f29a859125ebeac8c9461574743f70c2a7ef301b8505f7d036", - "https://deno.land/std@0.132.0/node/_fs/_fs_readFile.ts": "3eae6c930e08c1279d400c0f5a008e6d96949ff3a4f5bf7d43e1b94b94ce3854", - "https://deno.land/std@0.132.0/node/_fs/_fs_readdir.ts": "a546f01387b7c49ddc1bd78d0e123a9668c710c56cffb4d9577ef46703cab463", - "https://deno.land/std@0.132.0/node/_fs/_fs_readlink.ts": "00553cd155f3bea565ffe43d7f0c10d75e895455562e1e8ea153e8f4e7ac04c7", - "https://deno.land/std@0.132.0/node/_fs/_fs_realpath.ts": "3ec236e4ad3c171203043422939973b6a948200ec4802425db41fa60c860dde9", - "https://deno.land/std@0.132.0/node/_fs/_fs_rename.ts": "3be71e8f43275c349b7abb9343b6e6764df09fabcbd2d316f8ac170ea556c645", - "https://deno.land/std@0.132.0/node/_fs/_fs_rm.ts": "a9328f99d925d7c74d31361d466ca33475aa7c6d1d6f037a49ce1ed996f0a0b4", - "https://deno.land/std@0.132.0/node/_fs/_fs_rmdir.ts": "b74007891357e709b37e6721eb355a1c4f25575995bb7c961a3c40f03ebc624c", - "https://deno.land/std@0.132.0/node/_fs/_fs_stat.ts": "bd47ce0bfc2b867392abc6ec95878ab4f6dddb94af73903d6fa1a02ba3e26af8", - "https://deno.land/std@0.132.0/node/_fs/_fs_streams.ts": "0e54bd4e41b462a701d6729ea17db01624aa48109e402fea8eecf13be324cf16", - "https://deno.land/std@0.132.0/node/_fs/_fs_symlink.ts": "0bddc37c5092f847634bd41cee0b643b9c03fc541c0e635cf35da1fcb4d0f7fa", - "https://deno.land/std@0.132.0/node/_fs/_fs_truncate.ts": "e2d380f7a81f69c4d4db30c442558ba8d8dea561e5097af41022bb5724e494e5", - "https://deno.land/std@0.132.0/node/_fs/_fs_unlink.ts": "c537ca98e507972d65f0b113a179b5f5083f0da3e6f9fae29895fd2a9660c18a", - "https://deno.land/std@0.132.0/node/_fs/_fs_utimes.ts": "c4446b7e39bf6977eca4364360501a97b96db9ea41e0cdf49abddab73481a175", - "https://deno.land/std@0.132.0/node/_fs/_fs_watch.ts": "2338de777458021d39cb9f0a5f3ea1bd9109a7ca2c2ad6ec41029df1753838f8", - "https://deno.land/std@0.132.0/node/_fs/_fs_write.mjs": "8c130b8b9522e1e4b08e687eb27939240260c115fda1e38e99c57b4f3af6481f", - "https://deno.land/std@0.132.0/node/_fs/_fs_writeFile.ts": "79d176021c8ceae0d956763a33834166ebc3f1691ed9219a21674b2374f115c3", - "https://deno.land/std@0.132.0/node/_fs/_fs_writev.mjs": "274df0a109010862c8f8b320dc7784de9bd9425fe2a6afd05f1f06f547a25cba", - "https://deno.land/std@0.132.0/node/_next_tick.ts": "64c361f6bca21df2a72dd77b84bd49d80d97a694dd3080703bc78f52146351d1", - "https://deno.land/std@0.132.0/node/_options.ts": "27f3c1269a700d330cc046cf748aa9178b8fc39d1473de625688e07cb0eb9d28", - "https://deno.land/std@0.132.0/node/_process/exiting.ts": "bc9694769139ffc596f962087155a8bfef10101d03423b9dcbc51ce6e1f88fce", - "https://deno.land/std@0.132.0/node/_process/process.ts": "84644b184053835670f79652d1ce3312c9ad079c211e6207ebefeedf159352a3", - "https://deno.land/std@0.132.0/node/_process/stdio.mjs": "971c3b086040d8521562155db13f22f9971d5c42c852b2081d4d2f0d8b6ab6bd", - "https://deno.land/std@0.132.0/node/_process/streams.mjs": "555062e177ad05f887147651fdda25fa55098475fcf142c8d162b8fe14097bbb", - "https://deno.land/std@0.132.0/node/_stream.mjs": "07f6cbabaad0382fb4b9a25e70ac3093a44022b859247f64726746e6373f1c91", - "https://deno.land/std@0.132.0/node/_util/_util_callbackify.ts": "79928ad80df3e469f7dcdb198118a7436d18a9f6c08bd7a4382332ad25a718cf", - "https://deno.land/std@0.132.0/node/_utils.ts": "c2c352e83c4c96f5ff994b1c8246bff2abcb21bfc3f1c06162cb3af1d201e615", - "https://deno.land/std@0.132.0/node/buffer.ts": "fbecbf3f237fa49bec96e97ecf56a7b92d48037b3d11219288e68943cc921600", - "https://deno.land/std@0.132.0/node/crypto.ts": "fffbc3fc3dcc16ea986d3e89eed5f70db7dfef2c18d1205a8c8fe5327ee0192d", - "https://deno.land/std@0.132.0/node/dns.ts": "ae2abd1bc8ac79543fe4d702f2aa3607101dc788b6eeba06e06436cb42ee3779", - "https://deno.land/std@0.132.0/node/events.ts": "a1d40fc0dbccc944379ef968b80ea08f9fce579e88b5057fdb64e4f0812476dd", - "https://deno.land/std@0.132.0/node/fs.ts": "21a3189c460bd37ac3f6734e040587125b7c8435c0a9da4e6c57544a3aca81c2", - "https://deno.land/std@0.132.0/node/internal/assert.mjs": "118327c8866266534b30d3a36ad978204af7336dc2db3158b8167192918d4e06", - "https://deno.land/std@0.132.0/node/internal/async_hooks.ts": "8eca5b80f58ffb259e9b3a73536dc2fe2e67d07fd24bfe2aee325a4aa435edb3", - "https://deno.land/std@0.132.0/node/internal/blob.mjs": "52080b2f40b114203df67f8a6650f9fe3c653912b8b3ef2f31f029853df4db53", - "https://deno.land/std@0.132.0/node/internal/buffer.mjs": "6662fe7fe517329453545be34cea27a24f8ccd6d09afd4f609f11ade2b6dfca7", - "https://deno.land/std@0.132.0/node/internal/crypto/keys.ts": "16ce7b15a9fc5e4e3dee8fde75dae12f3d722558d5a1a6e65a9b4f86d64a21e9", - "https://deno.land/std@0.132.0/node/internal/crypto/util.mjs": "1de55a47fdbed6721b467a77ba48fdd1550c10b5eee77bbdb602eaffee365a5e", - "https://deno.land/std@0.132.0/node/internal/dtrace.ts": "50dd0e77b0269e47ff673bdb9ad0ef0ea3a3c53ac30c1695883ce4748e04ca14", - "https://deno.land/std@0.132.0/node/internal/error_codes.ts": "ac03c4eae33de3a69d6c98e8678003207eecf75a6900eb847e3fea3c8c9e6d8f", - "https://deno.land/std@0.132.0/node/internal/errors.ts": "25f91691225b001660e6e64745ecd336fbf562cf0185e8896ff013c2d0226794", - "https://deno.land/std@0.132.0/node/internal/fs/streams.ts": "c925db185efdf56c35cde8270c07d61698b80603a90e07caf1cb4ff80abf195b", - "https://deno.land/std@0.132.0/node/internal/fs/utils.mjs": "2a571ecbd169b444f07b7193306f108fdcb4bfd9b394b33716ad05edf30e899e", - "https://deno.land/std@0.132.0/node/internal/hide_stack_frames.ts": "a91962ec84610bc7ec86022c4593cdf688156a5910c07b5bcd71994225c13a03", - "https://deno.land/std@0.132.0/node/internal/idna.ts": "a8bdd28431f06630d8aad85d3cb8fd862459107af228c8805373ad2080f1c587", - "https://deno.land/std@0.132.0/node/internal/net.ts": "1239886cd2508a68624c2dae8abf895e8aa3bb15a748955349f9ac5539032238", - "https://deno.land/std@0.132.0/node/internal/normalize_encoding.mjs": "3779ec8a7adf5d963b0224f9b85d1bc974a2ec2db0e858396b5d3c2c92138a0a", - "https://deno.land/std@0.132.0/node/internal/process/per_thread.mjs": "a42b1dcfb009ad5039144a999a35a429e76112f9322febbe353eda9d1879d936", - "https://deno.land/std@0.132.0/node/internal/querystring.ts": "c3b23674a379f696e505606ddce9c6feabe9fc497b280c56705c340f4028fe74", - "https://deno.land/std@0.132.0/node/internal/stream_base_commons.ts": "934a9e69f46f2de644956edfa9fb040af7861e326fe5325dab38ef9caf2940bc", - "https://deno.land/std@0.132.0/node/internal/streams/_utils.ts": "77fceaa766679847e4d4c3c96b2573c00a790298d90551e8e4df1d5e0fdaad3b", - "https://deno.land/std@0.132.0/node/internal/streams/add-abort-signal.mjs": "5623b83fa64d439cc4a1f09ae47ec1db29512cc03479389614d8f62a37902f5e", - "https://deno.land/std@0.132.0/node/internal/streams/buffer_list.mjs": "c6a7b29204fae025ff5e9383332acaea5d44bc7c522a407a79b8f7a6bc6c312d", - "https://deno.land/std@0.132.0/node/internal/streams/compose.mjs": "b522daab35a80ae62296012a4254fd7edfc0366080ffe63ddda4e38fe6b6803e", - "https://deno.land/std@0.132.0/node/internal/streams/destroy.mjs": "9c9bbeb172a437041d529829f433df72cf0b63ae49f3ee6080a55ffbef7572ad", - "https://deno.land/std@0.132.0/node/internal/streams/duplex.mjs": "b014087cd04f79b8a4028d8b9423b987e07bbfacf3b5df518cb752ac3657580f", - "https://deno.land/std@0.132.0/node/internal/streams/end-of-stream.mjs": "38be76eaceac231dfde643e72bc0940625446bf6d1dbd995c91c5ba9fd59b338", - "https://deno.land/std@0.132.0/node/internal/streams/from.mjs": "134255c698ed63b33199911eb8e042f8f67e9682409bb11552e6120041ed1872", - "https://deno.land/std@0.132.0/node/internal/streams/legacy.mjs": "6ea28db95d4503447473e62f0b23ff473bfe1751223c33a3c5816652e93b257a", - "https://deno.land/std@0.132.0/node/internal/streams/passthrough.mjs": "a51074193b959f3103d94de41e23a78dfcff532bdba53af9146b86340d85eded", - "https://deno.land/std@0.132.0/node/internal/streams/pipeline.mjs": "9890b121759ede869174ef70c011fde964ca94d81f2ed97b8622d7cb17b49285", - "https://deno.land/std@0.132.0/node/internal/streams/readable.mjs": "a70c41171ae25c556b52785b0c178328014bd33d8c0e4d229d4adaac7414b6ca", - "https://deno.land/std@0.132.0/node/internal/streams/state.mjs": "9ef917392a9d8005a6e038260c5fd31518d2753aea0bc9e39824c199310434cb", - "https://deno.land/std@0.132.0/node/internal/streams/transform.mjs": "3b361abad2ac78f7ccb6f305012bafdc0e983dfa4bb6ecddb4626e34a781a5f5", - "https://deno.land/std@0.132.0/node/internal/streams/utils.mjs": "06c21d0db0d51f1bf1e3225a661c3c29909be80355d268e64ee5922fc5eb6c5e", - "https://deno.land/std@0.132.0/node/internal/streams/writable.mjs": "ad4e2b176ffdf752c8e678ead3a514679a5a8d652f4acf797115dceb798744d5", - "https://deno.land/std@0.132.0/node/internal/timers.mjs": "b43e24580cec2dd50f795e4342251a79515c0db21630c25b40fdc380a78b74e7", - "https://deno.land/std@0.132.0/node/internal/url.ts": "eacef0ace4f4c5394e9818a81499f4871b2a993d1bd3b902047e44a381ef0e22", - "https://deno.land/std@0.132.0/node/internal/util.mjs": "2f0c8ff553c175ea6e4ed13d7cd7cd6b86dc093dc2f783c6c3dfc63f60a0943e", - "https://deno.land/std@0.132.0/node/internal/util/comparisons.ts": "680b55fe8bdf1613633bc469fa0440f43162c76dbe36af9aa2966310e1bb9f6e", - "https://deno.land/std@0.132.0/node/internal/util/debuglog.ts": "99e91bdf26f6c67861031f684817e1705a5bc300e81346585b396f413387edfb", - "https://deno.land/std@0.132.0/node/internal/util/inspect.mjs": "d1c2569c66a3dab45eec03208f22ad4351482527859c0011a28a6c797288a0aa", - "https://deno.land/std@0.132.0/node/internal/util/types.ts": "b2dacb8f1f5d28a51c4da5c5b75172b7fcf694073ce95ca141323657e18b0c60", - "https://deno.land/std@0.132.0/node/internal/validators.mjs": "a7e82eafb7deb85c332d5f8d9ffef052f46a42d4a121eada4a54232451acc49a", - "https://deno.land/std@0.132.0/node/internal_binding/_libuv_winerror.ts": "801e05c2742ae6cd42a5f0fd555a255a7308a65732551e962e5345f55eedc519", - "https://deno.land/std@0.132.0/node/internal_binding/_node.ts": "e4075ba8a37aef4eb5b592c8e3807c39cb49ca8653faf8e01a43421938076c1b", - "https://deno.land/std@0.132.0/node/internal_binding/_utils.ts": "1c50883b5751a9ea1b38951e62ed63bacfdc9d69ea665292edfa28e1b1c5bd94", - "https://deno.land/std@0.132.0/node/internal_binding/_winerror.ts": "8811d4be66f918c165370b619259c1f35e8c3e458b8539db64c704fbde0a7cd2", - "https://deno.land/std@0.132.0/node/internal_binding/async_wrap.ts": "f06b8a403ad871248eb064190d27bf6fefdbe948991e71a18d7077390d5773f9", - "https://deno.land/std@0.132.0/node/internal_binding/buffer.ts": "722c62b85f966e0777b2d98c021b60e75d7f2c2dabc43413ef37d60dbd13a5d9", - "https://deno.land/std@0.132.0/node/internal_binding/cares_wrap.ts": "25b7b5d56612b2985260b673021828d6511a1c83b4c1927f5732cad2f2a718af", - "https://deno.land/std@0.132.0/node/internal_binding/config.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/connection_wrap.ts": "0380444ee94d5bd7b0b09921223d16729c9762a94e80b7f51eda49c7f42e6d0a", - "https://deno.land/std@0.132.0/node/internal_binding/constants.ts": "aff06aac49eda4234bd3a2b0b8e1fbfc67824e281c532ff9960831ab503014cc", - "https://deno.land/std@0.132.0/node/internal_binding/contextify.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/credentials.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/crypto.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/errors.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/fs.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/fs_dir.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/fs_event_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/handle_wrap.ts": "e59df84b1fb1b9823b09774b3e512d9c0029b4557400d09dd02cd7661c2c4830", - "https://deno.land/std@0.132.0/node/internal_binding/heap_utils.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/http_parser.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/icu.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/inspector.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/js_stream.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/messaging.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/mod.ts": "f68e74e8eed84eaa6b0de24f0f4c47735ed46866d7ee1c5a5e7c0667b4f0540f", - "https://deno.land/std@0.132.0/node/internal_binding/module_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/native_module.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/natives.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/node_file.ts": "c96ee0b2af319a3916de950a6c4b0d5fb00d09395c51cd239c54d95d62567aaf", - "https://deno.land/std@0.132.0/node/internal_binding/node_options.ts": "3cd5706153d28a4f5944b8b162c1c61b7b8e368a448fb1a2cff9f7957d3db360", - "https://deno.land/std@0.132.0/node/internal_binding/options.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/os.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/performance.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/pipe_wrap.ts": "00e942327f8e1c4b74a5888a82f0e16ba775cd09af804f96b6f6849b7eab1719", - "https://deno.land/std@0.132.0/node/internal_binding/process_methods.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/report.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/serdes.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/signal_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/spawn_sync.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/stream_wrap.ts": "d6e96f4b89d82ad5cc9b243c3d3880c9d85086165da54a7d85821a63491e5abf", - "https://deno.land/std@0.132.0/node/internal_binding/string_decoder.ts": "5cb1863763d1e9b458bc21d6f976f16d9c18b3b3f57eaf0ade120aee38fba227", - "https://deno.land/std@0.132.0/node/internal_binding/symbols.ts": "51cfca9bb6132d42071d4e9e6b68a340a7f274041cfcba3ad02900886e972a6c", - "https://deno.land/std@0.132.0/node/internal_binding/task_queue.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/tcp_wrap.ts": "10c64d5e092a8bff99cfe05adea716e4e52f4158662a5821790953e47e2cc21c", - "https://deno.land/std@0.132.0/node/internal_binding/timers.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/tls_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/trace_events.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/tty_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/types.ts": "4c26fb74ba2e45de553c15014c916df6789529a93171e450d5afb016b4c765e7", - "https://deno.land/std@0.132.0/node/internal_binding/udp_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/url.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/util.ts": "90364292e2bd598ab5d105b48ca49817b6708f2d1d9cbaf08b2b3ab5ca4c90a7", - "https://deno.land/std@0.132.0/node/internal_binding/uv.ts": "3821bc5e676d6955d68f581988c961d77dd28190aba5a9c59f16001a4deb34ba", - "https://deno.land/std@0.132.0/node/internal_binding/v8.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/worker.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/internal_binding/zlib.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", - "https://deno.land/std@0.132.0/node/net.ts": "dfcb7e412abb3d5c55edde7d823b0ccb9601f7d40555ae3c862810c78b176185", - "https://deno.land/std@0.132.0/node/os.ts": "943d3294a7a00f39491148cd2097cdbf101233a421262223bb20ae702c059df5", - "https://deno.land/std@0.132.0/node/path.ts": "c65858e9cbb52dbc0dd348eefcdc41e82906c39cfa7982f2d4d805e828414b8c", - "https://deno.land/std@0.132.0/node/path/_constants.ts": "bd26f24a052b7d6b746151f4a236d29ab3c2096883bb6449c2fa499494406672", - "https://deno.land/std@0.132.0/node/path/_interface.ts": "6034ee29f6f295460ec82db1a94df9269aecbb0eceb81be72e9d843f8e8a97e6", - "https://deno.land/std@0.132.0/node/path/_util.ts": "9d4735fc05f8f1fb94406450e84e23fd201dc3fef5298b009e44cfa4e797b8f0", - "https://deno.land/std@0.132.0/node/path/common.ts": "f41a38a0719a1e85aa11c6ba3bea5e37c15dd009d705bd8873f94c833568cbc4", - "https://deno.land/std@0.132.0/node/path/glob.ts": "d6b64a24f148855a6e8057a171a2f9910c39e492e4ccec482005205b28eb4533", - "https://deno.land/std@0.132.0/node/path/mod.ts": "62e21dc6e1fe2e9742fce85de631a7b067d968544fe66954578e6d73c97369a2", - "https://deno.land/std@0.132.0/node/path/posix.ts": "9dd5fc83c4ae0e0b700bef43c88c67e276840c187a66d4d6a661440cf1fecc52", - "https://deno.land/std@0.132.0/node/path/separator.ts": "c908c9c28ebe7f1fea67daaccf84b63af90d882fe986f9fa03af9563a852723a", - "https://deno.land/std@0.132.0/node/path/win32.ts": "f869ee449b6dee69b13e2d1f8f7f1d01c7ae1e67fa573eab789429929f7a3864", - "https://deno.land/std@0.132.0/node/process.ts": "699f47f2f177556e17e2f7d0dcd3705ff5065cbdf72029e534c1540404d6f501", - "https://deno.land/std@0.132.0/node/querystring.ts": "967b8a7b00a73ebe373666deb3a7e501f164bac27bb342fde7221ecbb3522689", - "https://deno.land/std@0.132.0/node/stream.ts": "d127faa074a9e3886e4a01dcfe9f9a6a4b5641f76f6acc356e8ded7da5dc2c81", - "https://deno.land/std@0.132.0/node/stream/promises.mjs": "b263c09f2d6bd715dc514fab3f99cca84f442e2d23e87adbe76e32ea46fc87e6", - "https://deno.land/std@0.132.0/node/string_decoder.ts": "51ce85a173d2e36ac580d418bb48b804adb41732fc8bd85f7d5d27b7accbc61f", - "https://deno.land/std@0.132.0/node/timers.ts": "2d66fcd21e37acf76c3a699a97230da57cc21382c8e885b3c5377b37efd0f06c", - "https://deno.land/std@0.132.0/node/url.ts": "bc0bde2774854b6a377c4c61fa73e5a28283cbeb7f8703479f44e471219c33a8", - "https://deno.land/std@0.132.0/node/util.ts": "7fd6933b37af89a8e64d73dc6ee1732455a59e7e6d0965311fbd73cd634ea630", - "https://deno.land/std@0.132.0/node/util/types.mjs": "f9288198cacd374b41bae7e92a23179d3160f4c0eaf14e19be3a4e7057219a60", - "https://deno.land/std@0.132.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", - "https://deno.land/std@0.132.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", - "https://deno.land/std@0.132.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", - "https://deno.land/std@0.132.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", - "https://deno.land/std@0.132.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", - "https://deno.land/std@0.132.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", - "https://deno.land/std@0.132.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", - "https://deno.land/std@0.132.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", - "https://deno.land/std@0.132.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", - "https://deno.land/std@0.132.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", - "https://deno.land/std@0.132.0/testing/_diff.ts": "9d849cd6877694152e01775b2d93f9d6b7aef7e24bfe3bfafc4d7a1ac8e9f392", - "https://deno.land/std@0.132.0/testing/asserts.ts": "b0ef969032882b1f7eb1c7571e313214baa1485f7b61cf35807b2434e254365c", - "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", - "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", - "https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8", - "https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2", - "https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c", - "https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c", - "https://deno.land/std@0.224.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", - "https://deno.land/std@0.224.0/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b", - "https://deno.land/std@0.224.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf", - "https://deno.land/std@0.224.0/path/_common/glob_to_reg_exp.ts": "6cac16d5c2dc23af7d66348a7ce430e5de4e70b0eede074bdbcf4903f4374d8d", - "https://deno.land/std@0.224.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", - "https://deno.land/std@0.224.0/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3", - "https://deno.land/std@0.224.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607", - "https://deno.land/std@0.224.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a", - "https://deno.land/std@0.224.0/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883", - "https://deno.land/std@0.224.0/path/_interface.ts": "8dfeb930ca4a772c458a8c7bbe1e33216fe91c253411338ad80c5b6fa93ddba0", - "https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15", - "https://deno.land/std@0.224.0/path/basename.ts": "7ee495c2d1ee516ffff48fb9a93267ba928b5a3486b550be73071bc14f8cc63e", - "https://deno.land/std@0.224.0/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643", - "https://deno.land/std@0.224.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36", - "https://deno.land/std@0.224.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c", - "https://deno.land/std@0.224.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441", - "https://deno.land/std@0.224.0/path/format.ts": "6ce1779b0980296cf2bc20d66436b12792102b831fd281ab9eb08fa8a3e6f6ac", - "https://deno.land/std@0.224.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069", - "https://deno.land/std@0.224.0/path/glob_to_regexp.ts": "7f30f0a21439cadfdae1be1bf370880b415e676097fda584a63ce319053b5972", - "https://deno.land/std@0.224.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7", - "https://deno.land/std@0.224.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141", - "https://deno.land/std@0.224.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a", - "https://deno.land/std@0.224.0/path/join_globs.ts": "5b3bf248b93247194f94fa6947b612ab9d3abd571ca8386cf7789038545e54a0", - "https://deno.land/std@0.224.0/path/mod.ts": "f6bd79cb08be0e604201bc9de41ac9248582699d1b2ee0ab6bc9190d472cf9cd", - "https://deno.land/std@0.224.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352", - "https://deno.land/std@0.224.0/path/normalize_glob.ts": "cc89a77a7d3b1d01053b9dcd59462b75482b11e9068ae6c754b5cf5d794b374f", - "https://deno.land/std@0.224.0/path/parse.ts": "77ad91dcb235a66c6f504df83087ce2a5471e67d79c402014f6e847389108d5a", - "https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d", - "https://deno.land/std@0.224.0/path/posix/basename.ts": "d2fa5fbbb1c5a3ab8b9326458a8d4ceac77580961b3739cd5bfd1d3541a3e5f0", - "https://deno.land/std@0.224.0/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", - "https://deno.land/std@0.224.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1", - "https://deno.land/std@0.224.0/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00", - "https://deno.land/std@0.224.0/path/posix/extname.ts": "e398c1d9d1908d3756a7ed94199fcd169e79466dd88feffd2f47ce0abf9d61d2", - "https://deno.land/std@0.224.0/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1", - "https://deno.land/std@0.224.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40", - "https://deno.land/std@0.224.0/path/posix/glob_to_regexp.ts": "76f012fcdb22c04b633f536c0b9644d100861bea36e9da56a94b9c589a742e8f", - "https://deno.land/std@0.224.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede", - "https://deno.land/std@0.224.0/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", - "https://deno.land/std@0.224.0/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63", - "https://deno.land/std@0.224.0/path/posix/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", - "https://deno.land/std@0.224.0/path/posix/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604", - "https://deno.land/std@0.224.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91", - "https://deno.land/std@0.224.0/path/posix/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", - "https://deno.land/std@0.224.0/path/posix/parse.ts": "09dfad0cae530f93627202f28c1befa78ea6e751f92f478ca2cc3b56be2cbb6a", - "https://deno.land/std@0.224.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c", - "https://deno.land/std@0.224.0/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf", - "https://deno.land/std@0.224.0/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf", - "https://deno.land/std@0.224.0/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0", - "https://deno.land/std@0.224.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add", - "https://deno.land/std@0.224.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d", - "https://deno.land/std@0.224.0/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b", - "https://deno.land/std@0.224.0/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40", - "https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808", - "https://deno.land/std@0.224.0/path/windows/basename.ts": "6bbc57bac9df2cec43288c8c5334919418d784243a00bc10de67d392ab36d660", - "https://deno.land/std@0.224.0/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", - "https://deno.land/std@0.224.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5", - "https://deno.land/std@0.224.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9", - "https://deno.land/std@0.224.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef", - "https://deno.land/std@0.224.0/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6", - "https://deno.land/std@0.224.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01", - "https://deno.land/std@0.224.0/path/windows/glob_to_regexp.ts": "e45f1f89bf3fc36f94ab7b3b9d0026729829fabc486c77f414caebef3b7304f8", - "https://deno.land/std@0.224.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a", - "https://deno.land/std@0.224.0/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", - "https://deno.land/std@0.224.0/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf", - "https://deno.land/std@0.224.0/path/windows/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", - "https://deno.land/std@0.224.0/path/windows/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604", - "https://deno.land/std@0.224.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780", - "https://deno.land/std@0.224.0/path/windows/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", - "https://deno.land/std@0.224.0/path/windows/parse.ts": "08804327b0484d18ab4d6781742bf374976de662f8642e62a67e93346e759707", - "https://deno.land/std@0.224.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7", - "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972", - "https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e", - "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c", - "https://deno.land/x/postgresjs@v3.4.0/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", - "https://deno.land/x/postgresjs@v3.4.0/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", - "https://deno.land/x/postgresjs@v3.4.0/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", - "https://deno.land/x/postgresjs@v3.4.0/src/connection.js": "3f22eea8d1150e4c3310b41bfe4477beb1063051042fccc25500c01d7b23648e", - "https://deno.land/x/postgresjs@v3.4.0/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", - "https://deno.land/x/postgresjs@v3.4.0/src/index.js": "ae897f92a0913e2b05b1f706f07bca35ace89df3ab4e7382b613e1469c1e1800", - "https://deno.land/x/postgresjs@v3.4.0/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", - "https://deno.land/x/postgresjs@v3.4.0/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", - "https://deno.land/x/postgresjs@v3.4.0/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", - "https://deno.land/x/postgresjs@v3.4.0/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", - "https://deno.land/x/postgresjs@v3.4.0/src/subscribe.js": "6ac0679e186ab4e681c55252ff50ac4e567df47a7d1382e6e37928f3e9f1fff8", - "https://deno.land/x/postgresjs@v3.4.0/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", - "https://deno.land/x/postgresjs@v3.4.2/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", - "https://deno.land/x/postgresjs@v3.4.2/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", - "https://deno.land/x/postgresjs@v3.4.2/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", - "https://deno.land/x/postgresjs@v3.4.2/src/connection.js": "4e43c34284c63c1895becdca54a900261254d9761e999888698ef989e9399132", - "https://deno.land/x/postgresjs@v3.4.2/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", - "https://deno.land/x/postgresjs@v3.4.2/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", - "https://deno.land/x/postgresjs@v3.4.2/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", - "https://deno.land/x/postgresjs@v3.4.2/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", - "https://deno.land/x/postgresjs@v3.4.2/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", - "https://deno.land/x/postgresjs@v3.4.2/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", - "https://deno.land/x/postgresjs@v3.4.2/src/subscribe.js": "6ac0679e186ab4e681c55252ff50ac4e567df47a7d1382e6e37928f3e9f1fff8", - "https://deno.land/x/postgresjs@v3.4.2/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", - "https://deno.land/x/postgresjs@v3.4.3/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", - "https://deno.land/x/postgresjs@v3.4.3/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", - "https://deno.land/x/postgresjs@v3.4.3/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", - "https://deno.land/x/postgresjs@v3.4.3/src/connection.js": "506df20bbaa44256e8511ee87e5f18e6343c406f7b9804c20dd182c1b5b4270b", - "https://deno.land/x/postgresjs@v3.4.3/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", - "https://deno.land/x/postgresjs@v3.4.3/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", - "https://deno.land/x/postgresjs@v3.4.3/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", - "https://deno.land/x/postgresjs@v3.4.3/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", - "https://deno.land/x/postgresjs@v3.4.3/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", - "https://deno.land/x/postgresjs@v3.4.3/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", - "https://deno.land/x/postgresjs@v3.4.3/src/subscribe.js": "6ac0679e186ab4e681c55252ff50ac4e567df47a7d1382e6e37928f3e9f1fff8", - "https://deno.land/x/postgresjs@v3.4.3/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", - "https://deno.land/x/postgresjs@v3.4.4/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", - "https://deno.land/x/postgresjs@v3.4.4/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", - "https://deno.land/x/postgresjs@v3.4.4/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", - "https://deno.land/x/postgresjs@v3.4.4/src/connection.js": "c63d53a0f35a7eb2670befef551f23fe914bbe9f0590de974e3e210c50527a29", - "https://deno.land/x/postgresjs@v3.4.4/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", - "https://deno.land/x/postgresjs@v3.4.4/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", - "https://deno.land/x/postgresjs@v3.4.4/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", - "https://deno.land/x/postgresjs@v3.4.4/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", - "https://deno.land/x/postgresjs@v3.4.4/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", - "https://deno.land/x/postgresjs@v3.4.4/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", - "https://deno.land/x/postgresjs@v3.4.4/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", - "https://deno.land/x/postgresjs@v3.4.4/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", - "https://deno.land/x/postgresjs@v3.4.5/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", - "https://deno.land/x/postgresjs@v3.4.5/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", - "https://deno.land/x/postgresjs@v3.4.5/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", - "https://deno.land/x/postgresjs@v3.4.5/src/connection.js": "e63062451fb6a7284c14540b3f268d1373c3028fb0f3b234056ad56569190e8f", - "https://deno.land/x/postgresjs@v3.4.5/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", - "https://deno.land/x/postgresjs@v3.4.5/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", - "https://deno.land/x/postgresjs@v3.4.5/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", - "https://deno.land/x/postgresjs@v3.4.5/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", - "https://deno.land/x/postgresjs@v3.4.5/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", - "https://deno.land/x/postgresjs@v3.4.5/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", - "https://deno.land/x/postgresjs@v3.4.5/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", - "https://deno.land/x/postgresjs@v3.4.5/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", - "https://deno.land/x/postgresjs@v3.4.6/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", - "https://deno.land/x/postgresjs@v3.4.6/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", - "https://deno.land/x/postgresjs@v3.4.6/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", - "https://deno.land/x/postgresjs@v3.4.6/src/connection.js": "3e93a3637a113ec558de83f65840ebfb50fe18e7e3e564df93b09a320df44a1f", - "https://deno.land/x/postgresjs@v3.4.6/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", - "https://deno.land/x/postgresjs@v3.4.6/src/index.js": "ef6bbdf3778c7b5c1b539da4ce2df67640ffb3b930dccfc7e2e2dde559333794", - "https://deno.land/x/postgresjs@v3.4.6/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", - "https://deno.land/x/postgresjs@v3.4.6/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", - "https://deno.land/x/postgresjs@v3.4.6/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", - "https://deno.land/x/postgresjs@v3.4.6/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", - "https://deno.land/x/postgresjs@v3.4.6/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", - "https://deno.land/x/postgresjs@v3.4.6/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", - "https://deno.land/x/postgresjs@v3.4.7/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", - "https://deno.land/x/postgresjs@v3.4.7/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", - "https://deno.land/x/postgresjs@v3.4.7/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", - "https://deno.land/x/postgresjs@v3.4.7/src/connection.js": "6bca72f956871acc34ddae638169161c207cd7b4127da464d41b96775d5fe8eb", - "https://deno.land/x/postgresjs@v3.4.7/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", - "https://deno.land/x/postgresjs@v3.4.7/src/index.js": "ef6bbdf3778c7b5c1b539da4ce2df67640ffb3b930dccfc7e2e2dde559333794", - "https://deno.land/x/postgresjs@v3.4.7/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", - "https://deno.land/x/postgresjs@v3.4.7/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", - "https://deno.land/x/postgresjs@v3.4.7/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", - "https://deno.land/x/postgresjs@v3.4.7/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", - "https://deno.land/x/postgresjs@v3.4.7/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", - "https://deno.land/x/postgresjs@v3.4.7/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed" - }, - "workspace": { - "dependencies": [ - "jsr:@opentelemetry/api@^1.9.0", - "jsr:@rabbit-company/rate-limiter@3", - "jsr:@std/assert@^1.0.14", - "jsr:@std/collections@^1.1.3", - "jsr:@std/data-structures@^1.0.9", - "jsr:@std/fmt@^1.0.8", - "jsr:@std/testing@^1.0.16", - "npm:@actions/core@^1.11.1", - "npm:@actions/github@^6.0.1", - "npm:@libpg-query/parser@^17.6.3", - "npm:@pgsql/types@^17.6.1", - "npm:@query-doctor/core@0.4", - "npm:@testcontainers/postgresql@^11.9.0", - "npm:@types/node@^24.9.1", - "npm:@types/nunjucks@^3.2.6", - "npm:async-sema@^3.1.1", - "npm:chokidar@^4.0.3", - "npm:dedent@^1.6.0", - "npm:fast-csv@^5.0.5", - "npm:jsondiffpatch@~0.7.3", - "npm:nunjucks@^3.2.4", - "npm:pgsql-deparser@^17.11.1", - "npm:prettier-plugin-sql@0.19", - "npm:prettier@3", - "npm:sql-highlight@^6.1.0", - "npm:zod@^4.1.12" - ] - } -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5eee983 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5371 @@ +{ + "name": "@query-doctor/analyzer", + "version": "0.1.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@query-doctor/analyzer", + "version": "0.1.1", + "dependencies": { + "@actions/core": "^1.11.1", + "@actions/github": "^6.0.1", + "@fastify/cors": "^11.2.0", + "@fastify/rate-limit": "^10.3.0", + "@fastify/websocket": "^11.2.0", + "@libpg-query/parser": "^17.6.3", + "@opentelemetry/api": "^1.9.0", + "@pgsql/types": "^17.6.1", + "@query-doctor/core": "^0.4.0", + "async-sema": "^3.1.1", + "dedent": "^1.6.0", + "fast-csv": "^5.0.5", + "fastify": "^5.3.3", + "jsondiffpatch": "^0.7.3", + "nunjucks": "^3.2.4", + "pg": "^8.16.0", + "pg-cursor": "^2.12.3", + "pgsql-deparser": "^17.11.1", + "prettier": "^3", + "prettier-plugin-sql": "^0.19", + "sql-highlight": "^6.1.0", + "zod": "^4.1.12" + }, + "devDependencies": { + "@testcontainers/postgresql": "^11.9.0", + "@types/node": "^24.9.1", + "@types/nunjucks": "^3.2.6", + "@types/pg": "^8.15.4", + "@types/pg-cursor": "^2.7.2", + "@types/ws": "^8.18.1", + "esbuild": "^0.27.3", + "tsx": "^4.21.0", + "typescript": "^5.8.3", + "vitest": "^3.1.4" + } + }, + "node_modules/@actions/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", + "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", + "license": "MIT", + "dependencies": { + "@actions/exec": "^1.1.1", + "@actions/http-client": "^2.0.1" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "license": "MIT", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/github": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz", + "integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==", + "license": "MIT", + "dependencies": { + "@actions/http-client": "^2.2.0", + "@octokit/core": "^5.0.1", + "@octokit/plugin-paginate-rest": "^9.2.2", + "@octokit/plugin-rest-endpoint-methods": "^10.4.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "undici": "^5.28.5" + } + }, + "node_modules/@actions/http-client": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", + "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", + "license": "MIT" + }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@dmsnell/diff-match-patch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@dmsnell/diff-match-patch/-/diff-match-patch-1.1.0.tgz", + "integrity": "sha512-yejLPmM5pjsGvxS9gXablUSbInW7H976c/FJ4iQxWIm7/38xBySRemTPDe34lhg1gVLbJntX0+sH0jYfU+PN9A==", + "license": "Apache-2.0" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fast-csv/format": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-5.0.5.tgz", + "integrity": "sha512-0P9SJXXnqKdmuWlLaTelqbrfdgN37Mvrb369J6eNmqL41IEIZQmV4sNM4GgAK2Dz3aH04J0HKGDMJFkYObThTw==", + "license": "MIT", + "dependencies": { + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/parse": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-5.0.5.tgz", + "integrity": "sha512-M0IbaXZDbxfOnpVE5Kps/a6FGlILLhtLsvWd9qNH3d2TxNnpbNkFf3KD26OmJX6MHq7PdQAl5htStDwnuwHx6w==", + "license": "MIT", + "dependencies": { + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", + "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@fastify/cors": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.2.0.tgz", + "integrity": "sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/@fastify/error": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", + "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", + "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0" + } + }, + "node_modules/@fastify/forwarded": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", + "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", + "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" + } + }, + "node_modules/@fastify/rate-limit": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-10.3.0.tgz", + "integrity": "sha512-eIGkG9XKQs0nyynatApA3EVrojHOuq4l6fhB4eeCk4PIOeadvOJz9/4w3vGI44Go17uaXOWEcPkaD8kuKm7g6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@lukeed/ms": "^2.0.2", + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/@fastify/websocket": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-11.2.0.tgz", + "integrity": "sha512-3HrDPbAG1CzUCqnslgJxppvzaAZffieOVbLp1DAy1huCSynUWPifSvfdEDUR8HlJLp3sp1A36uOM2tJogADS8w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "duplexify": "^4.1.3", + "fastify-plugin": "^5.0.0", + "ws": "^8.16.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@launchql/protobufjs": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/@launchql/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-vwi1nG2/heVFsIMHQU1KxTjUp5c757CTtRAZn/jutApCkFlle1iv8tzM/DHlSZJKDldxaYqnNYTg0pTyp8Bbtg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@libpg-query/parser": { + "version": "17.6.3", + "resolved": "https://registry.npmjs.org/@libpg-query/parser/-/parser-17.6.3.tgz", + "integrity": "sha512-AvbS7b9GJZfCzqt4tLMqTaYK7Css9pJRTA2dKWLoTlob/XO2VNc30Q3g9DmxNBmokVTrmibkn/dy9bw8hfosQQ==", + "license": "LICENSE IN LICENSE", + "dependencies": { + "@launchql/protobufjs": "7.2.6", + "@pgsql/types": "^17.6.0" + } + }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pgsql/types": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/@pgsql/types/-/types-17.6.2.tgz", + "integrity": "sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==", + "license": "MIT" + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@query-doctor/core": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@query-doctor/core/-/core-0.4.0.tgz", + "integrity": "sha512-d04ea11S9/hCkhXABFIbcNHP0uBzZ5WjFnCmYmjl3Yzl9XNm4KosJjI0jngnOQKuwSwNii0vvdYMxuOfMUiJNw==", + "dependencies": { + "@pgsql/types": "^17.6.2", + "colorette": "^2.0.20", + "dedent": "^1.7.1", + "pgsql-deparser": "^17.17.2", + "zod": "^4.1.13" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@testcontainers/postgresql": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.11.0.tgz", + "integrity": "sha512-Og64I/h5LKLVvUTkAcLeTXfFcMhh3dCHCypN3Uzd+tQMd70SpCfQ0LCP9v/U+MS7JBRzU9EmqhUFkTOm4hyZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^11.11.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.47", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", + "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz", + "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/nunjucks": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.2.6.tgz", + "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/pegjs": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@types/pegjs/-/pegjs-0.10.6.tgz", + "integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==", + "license": "MIT" + }, + "node_modules/@types/pg": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.16.0.tgz", + "integrity": "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-cursor": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/pg-cursor/-/pg-cursor-2.7.2.tgz", + "integrity": "sha512-m3xT8bVFCvx98LuzbvXyuCdT/Hjdd/v8ml4jL4K1QF70Y8clOfCFdgoaEB1FWdcSwcpoFYZTJQaMD9/GQ27efQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/pg": "*" + } + }, + "node_modules/@types/ssh2": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", + "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2-streams": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.13.tgz", + "integrity": "sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ssh2/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/avvio": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", + "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" + } + }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.4.tgz", + "integrity": "sha512-POK4oplfA7P7gqvetNmCs4CNtm9fNsx+IAh7jH7GgU0OJdge2rso0R20TNWVq6VoWcCvsTdlNDaleLHGaKx8CA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "license": "Apache-2.0" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buildcheck": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", + "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "license": "ISC" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "license": "MIT" + }, + "node_modules/docker-compose": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-1.3.1.tgz", + "integrity": "sha512-rF0wH69G3CCcmkN9J1RVMQBaKe8o77LT/3XmqcLIltWWVxcWAzp2TnO7wS3n/umZHN3/EVrlT3exSBMal+Ou1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/docker-modem": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz", + "integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.15.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.9.tgz", + "integrity": "sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@grpc/grpc-js": "^1.11.1", + "@grpc/proto-loader": "^0.7.13", + "docker-modem": "^5.0.6", + "protobufjs": "^7.3.2", + "tar-fs": "^2.1.4", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/dockerode/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-csv": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-5.0.5.tgz", + "integrity": "sha512-9//QpogDIPln5Dc8e3Q3vbSSLXlTeU7z1JqsUOXZYOln8EIn/OOO8+NS2c3ukR6oYngDd3+P1HXSkby3kNV9KA==", + "license": "MIT", + "dependencies": { + "@fast-csv/format": "5.0.5", + "@fast-csv/parse": "5.0.5" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stringify": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.3.0.tgz", + "integrity": "sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0", + "json-schema-ref-resolver": "^3.0.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastify": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.7.4.tgz", + "integrity": "sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^4.0.5", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^6.0.0", + "find-my-way": "^9.0.0", + "light-my-request": "^6.0.0", + "pino": "^10.1.0", + "process-warning": "^5.0.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastify-plugin": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz", + "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-my-way": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.4.0.tgz", + "integrity": "sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-ref-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", + "integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jsondiffpatch": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.7.3.tgz", + "integrity": "sha512-zd4dqFiXSYyant2WgSXAZ9+yYqilNVvragVNkNRn2IFZKgjyULNrKRznqN4Zon0MkLueCg+3QaPVCnDAVP20OQ==", + "license": "MIT", + "dependencies": { + "@dmsnell/diff-match-patch": "^1.1.0" + }, + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/jsox": { + "version": "1.2.125", + "resolved": "https://registry.npmjs.org/jsox/-/jsox-1.2.125.tgz", + "integrity": "sha512-HIf1uwublnXZsy7p3yHTrhzMzrLO6xKnqXytT9pEil5QxaXi8eyer7Is4luF5hYSV4kD3v03Y32FWoAeVYTghQ==", + "license": "MIT", + "bin": { + "jsox": "lib/cli.js" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/light-my-request": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", + "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" + } + }, + "node_modules/light-my-request/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "license": "MIT" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "license": "MIT" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", + "license": "BSD-3-Clause" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nan": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.25.0.tgz", + "integrity": "sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "license": "MIT", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, + "node_modules/nearley/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/node-sql-parser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-5.4.0.tgz", + "integrity": "sha512-jVe6Z61gPcPjCElPZ6j8llB3wnqGcuQzefim1ERsqIakxnEy5JlzV7XKdO1KmacRG5TKwPc4vJTgSRQ0LfkbFw==", + "license": "Apache-2.0", + "dependencies": { + "@types/pegjs": "^0.10.0", + "big-integer": "^1.6.48" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nunjucks": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", + "license": "BSD-2-Clause", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/pg": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", + "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.11.0", + "pg-pool": "^3.11.0", + "pg-protocol": "^1.11.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz", + "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==", + "license": "MIT" + }, + "node_modules/pg-cursor": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.17.0.tgz", + "integrity": "sha512-2Uio3Xfl5ldwJfls+RgGL+YbPcKQncWACWjYQFqlamvHZ4HJFjZhhZBbqd7jQ2LIkZYSvU90bm2dNW0rno+QFQ==", + "license": "MIT", + "peerDependencies": { + "pg": "^8" + } + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz", + "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", + "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/pgsql-deparser": { + "version": "17.17.2", + "resolved": "https://registry.npmjs.org/pgsql-deparser/-/pgsql-deparser-17.17.2.tgz", + "integrity": "sha512-FCjqKY3Sdmce3VUd3CxCXF0kqaZ0s4a6yIMT5UJ9vETh0cF54A8Tpqjn0qBKaPUD8xqTKeLdS+SfiwjAC64wrA==", + "license": "MIT", + "dependencies": { + "@pgsql/types": "^17.6.2" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-sql": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-sql/-/prettier-plugin-sql-0.19.2.tgz", + "integrity": "sha512-DAu1Jcanpvs32OAOXsqaVXOpPs4nFLVkB3XwzRiZZVNL5/c+XdlNxWFMiMpMhYhmCG5BW3srK8mhikCOv5tPfg==", + "license": "MIT", + "dependencies": { + "jsox": "^1.2.123", + "node-sql-parser": "^5.3.10", + "sql-formatter": "^15.6.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + }, + "peerDependencies": { + "prettier": "^3.0.3" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "license": "CC0-1.0" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "license": "MIT", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/randexp/node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex2": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", + "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ret": "~0.5.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sql-formatter": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.7.0.tgz", + "integrity": "sha512-o2yiy7fYXK1HvzA8P6wwj8QSuwG3e/XcpWht/jIxkQX99c0SVPw0OXdLSV9fHASPiYB09HLA0uq8hokGydi/QA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "nearley": "^2.20.1" + }, + "bin": { + "sql-formatter": "bin/sql-formatter-cli.cjs" + } + }, + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/ssh-remote-port-forward": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", + "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ssh2": "^0.5.48", + "ssh2": "^1.4.0" + } + }, + "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { + "version": "0.5.52", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", + "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2-streams": "*" + } + }, + "node_modules/ssh2": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.23.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tar-fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/testcontainers": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.11.0.tgz", + "integrity": "sha512-nKTJn3n/gkyGg/3SVkOwX+isPOGSHlfI+CWMobSmvQrsj7YW01aWvl2pYIfV4LMd+C8or783yYrzKSK2JlP+Qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.47", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.4.3", + "docker-compose": "^1.3.0", + "dockerode": "^4.0.9", + "get-port": "^7.1.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.1.1", + "tmp": "^0.2.5", + "undici": "^7.16.0" + } + }, + "node_modules/testcontainers/node_modules/undici": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz", + "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/thread-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz", + "integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "license": "ISC" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c21bc21 --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "@query-doctor/analyzer", + "version": "0.1.1", + "private": true, + "type": "module", + "scripts": { + "start": "node --import tsx src/main.ts", + "start:dev": "node --import tsx --watch src/main.ts", + "dev": "node --env-file=.env --import tsx --watch src/main.ts", + "test": "vitest", + "typecheck": "tsc --noEmit", + "build": "esbuild src/main.ts --bundle --platform=node --format=esm --outfile=dist/main.mjs --packages=external && cp src/reporters/github/success.md.j2 src/sync/schema_dump.sql dist/" + }, + "dependencies": { + "@actions/core": "^1.11.1", + "@actions/github": "^6.0.1", + "@fastify/cors": "^11.2.0", + "@fastify/rate-limit": "^10.3.0", + "@fastify/websocket": "^11.2.0", + "@libpg-query/parser": "^17.6.3", + "@opentelemetry/api": "^1.9.0", + "@pgsql/types": "^17.6.1", + "@query-doctor/core": "^0.4.0", + "async-sema": "^3.1.1", + "dedent": "^1.6.0", + "fast-csv": "^5.0.5", + "fastify": "^5.3.3", + "jsondiffpatch": "^0.7.3", + "nunjucks": "^3.2.4", + "pg": "^8.16.0", + "pg-cursor": "^2.12.3", + "pgsql-deparser": "^17.11.1", + "prettier": "^3", + "prettier-plugin-sql": "^0.19", + "sql-highlight": "^6.1.0", + "zod": "^4.1.12" + }, + "devDependencies": { + "@testcontainers/postgresql": "^11.9.0", + "@types/node": "^24.9.1", + "@types/nunjucks": "^3.2.6", + "@types/pg": "^8.15.4", + "@types/pg-cursor": "^2.7.2", + "@types/ws": "^8.18.1", + "esbuild": "^0.27.3", + "tsx": "^4.21.0", + "typescript": "^5.8.3", + "vitest": "^3.1.4" + } +} diff --git a/src/env.ts b/src/env.ts index 6da00d2..09f2a3c 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,5 +1,4 @@ import { z } from "zod"; -import { mapValues } from "@std/collections"; const envSchema = z.object({ CI: z.stringbool().default(false), @@ -21,5 +20,7 @@ const envSchema = z.object({ // we want to avoid asking for ALL env permissions if possible export const env = envSchema.parse( - mapValues(envSchema.shape, (_, key) => Deno.env.get(key)), + Object.fromEntries( + Object.keys(envSchema.shape).map((key) => [key, process.env[key]]), + ), ); diff --git a/src/main.ts b/src/main.ts index 1574ea3..9404b24 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,8 +3,8 @@ import { Runner } from "./runner.ts"; import { env } from "./env.ts"; import { log } from "./log.ts"; import { createServer } from "./server/http.ts"; -import { shutdown } from "./shutdown.ts"; import { Connectable } from "./sync/connectable.ts"; +import { shutdownController } from "./shutdown.ts"; async function runInCI( postgresUrl: Connectable, @@ -22,31 +22,42 @@ async function runInCI( await runner.run(); } -function runOutsideCI() { - const os = Deno.build.os; - const arch = Deno.build.arch; +async function runOutsideCI() { + const os = process.platform; + const arch = process.arch; log.info( `Starting server (${os}-${arch}) on ${env.HOST}:${env.PORT}`, "main", ); if (!env.POSTGRES_URL) { core.setFailed("POSTGRES_URL environment variable is not set"); - Deno.exit(1); + process.exit(1); } - createServer(env.HOST, env.PORT, Connectable.fromString(env.POSTGRES_URL)); + const server = await createServer( + env.HOST, + env.PORT, + Connectable.fromString(env.POSTGRES_URL), + ); + + const shutdown = async () => { + shutdownController.abort(); + await server.close(); + process.exit(0); + }; + + process.on("SIGTERM", shutdown); + process.on("SIGINT", shutdown); } async function main() { - Deno.addSignalListener("SIGTERM", shutdown); - Deno.addSignalListener("SIGINT", shutdown); if (env.CI) { if (!env.POSTGRES_URL) { core.setFailed("POSTGRES_URL environment variable is not set"); - Deno.exit(1); + process.exit(1); } if (!env.LOG_PATH) { core.setFailed("LOG_PATH environment variable is not set"); - Deno.exit(1); + process.exit(1); } await runInCI( Connectable.fromString(env.POSTGRES_URL), @@ -54,12 +65,10 @@ async function main() { env.STATISTICS_PATH, typeof env.MAX_COST === "number" ? env.MAX_COST : undefined, ); - Deno.exit(); + process.exit(); } else { - runOutsideCI(); + await runOutsideCI(); } } -if (import.meta.main) { - await main(); -} +await main(); diff --git a/src/remote/disabled-indexes.test.ts b/src/remote/disabled-indexes.test.ts index 0b1762f..6152848 100644 --- a/src/remote/disabled-indexes.test.ts +++ b/src/remote/disabled-indexes.test.ts @@ -1,54 +1,54 @@ -import { assertEquals } from "@std/assert/equals"; +import { test, expect } from "vitest"; import { DisabledIndexes } from "./disabled-indexes.ts"; import { PgIdentifier } from "@query-doctor/core"; -Deno.test("DisabledIndexes.add adds an index", () => { +test("DisabledIndexes.add adds an index", () => { const indexes = new DisabledIndexes(); const indexName = PgIdentifier.fromString("my_index"); indexes.add(indexName); const result = [...indexes]; - assertEquals(result.length, 1); - assertEquals(result[0].toString(), "my_index"); + expect(result.length).toEqual(1); + expect(result[0]!.toString()).toEqual("my_index"); }); -Deno.test("DisabledIndexes.remove removes an existing index", () => { +test("DisabledIndexes.remove removes an existing index", () => { const indexes = new DisabledIndexes(); const indexName = PgIdentifier.fromString("my_index"); indexes.add(indexName); const removed = indexes.remove(indexName); - assertEquals(removed, true); - assertEquals([...indexes].length, 0); + expect(removed).toEqual(true); + expect([...indexes].length).toEqual(0); }); -Deno.test("DisabledIndexes.remove returns false for non-existent index", () => { +test("DisabledIndexes.remove returns false for non-existent index", () => { const indexes = new DisabledIndexes(); const indexName = PgIdentifier.fromString("my_index"); const removed = indexes.remove(indexName); - assertEquals(removed, false); + expect(removed).toEqual(false); }); -Deno.test("DisabledIndexes.toggle disables an enabled index", () => { +test("DisabledIndexes.toggle disables an enabled index", () => { const indexes = new DisabledIndexes(); const indexName = PgIdentifier.fromString("my_index"); const isDisabled = indexes.toggle(indexName); - assertEquals(isDisabled, true); - assertEquals([...indexes].length, 1); + expect(isDisabled).toEqual(true); + expect([...indexes].length).toEqual(1); }); -Deno.test("DisabledIndexes.toggle enables a disabled index", () => { +test("DisabledIndexes.toggle enables a disabled index", () => { const indexes = new DisabledIndexes(); const indexName = PgIdentifier.fromString("my_index"); indexes.add(indexName); const isDisabled = indexes.toggle(indexName); - assertEquals(isDisabled, false); - assertEquals([...indexes].length, 0); + expect(isDisabled).toEqual(false); + expect([...indexes].length).toEqual(0); }); -Deno.test("DisabledIndexes iterator returns PgIdentifier instances", () => { +test("DisabledIndexes iterator returns PgIdentifier instances", () => { const indexes = new DisabledIndexes(); indexes.add(PgIdentifier.fromString("index_a")); indexes.add(PgIdentifier.fromString("index_b")); const result = [...indexes]; - assertEquals(result.length, 2); - assertEquals(result.map((i) => i.toString()).sort(), ["index_a", "index_b"]); + expect(result.length).toEqual(2); + expect(result.map((i) => i.toString()).sort()).toEqual(["index_a", "index_b"]); }); diff --git a/src/remote/disabled-indexes.ts b/src/remote/disabled-indexes.ts index d234d72..82253aa 100644 --- a/src/remote/disabled-indexes.ts +++ b/src/remote/disabled-indexes.ts @@ -32,9 +32,9 @@ export class DisabledIndexes { return false; } - [Symbol.iterator](): Iterator { - return this.disabledIndexNames.values().map((indexName) => - PgIdentifier.fromString(indexName) - ); + *[Symbol.iterator](): Iterator { + for (const indexName of this.disabledIndexNames) { + yield PgIdentifier.fromString(indexName); + } } } diff --git a/src/remote/gin-indexes.test.ts b/src/remote/gin-indexes.test.ts index 3807dde..41ec425 100644 --- a/src/remote/gin-indexes.test.ts +++ b/src/remote/gin-indexes.test.ts @@ -1,8 +1,9 @@ +import { test, expect, vi, afterEach } from "vitest"; import { PostgreSqlContainer } from "@testcontainers/postgresql"; import { QueryOptimizer } from "./query-optimizer.ts"; import { ConnectionManager } from "../sync/connection-manager.ts"; import { Connectable } from "../sync/connectable.ts"; -import { assert, assertEquals, assertGreater } from "@std/assert"; + import { type OptimizedQuery } from "../sql/recent-query.ts"; function hasGinRecommendation(query: OptimizedQuery): boolean { @@ -30,11 +31,7 @@ function getBtreeRecommendations(query: OptimizedQuery) { // 1. Basic @> containment → GIN jsonb_path_ops // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: basic @> containment recommends GIN with jsonb_path_ops", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: basic @> containment recommends GIN with jsonb_path_ops", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -99,31 +96,26 @@ Deno.test({ const match = improvements.find((q) => q.query.includes("@>") || q.query.includes("products") ); - assert(match, "Expected improvements for @> containment query"); - assert(hasGinRecommendation(match), "Expected a GIN index recommendation"); - - const ginRecs = getGinRecommendations(match); - assert( - ginRecs.some((r) => - r.definition.toLowerCase().includes("jsonb_path_ops") - ), + expect(match, "Expected improvements for @> containment query").toBeTruthy(); + expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + + const ginRecs = getGinRecommendations(match!); + expect( + ginRecs.some((r) => r.definition.toLowerCase().includes("jsonb_path_ops")), `Expected jsonb_path_ops, got: ${ginRecs.map((r) => r.definition)}`, - ); + ).toBeTruthy(); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── // 2. Key existence ? → GIN default jsonb_ops // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: key existence (?) recommends GIN with default jsonb_ops", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: key existence (?) recommends GIN with default jsonb_ops", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -189,34 +181,27 @@ Deno.test({ const match = improvements.find((q) => q.query.includes("?") || q.query.includes("payload") ); - assert(match, "Expected improvements for ? key existence query"); - assert(hasGinRecommendation(match), "Expected a GIN index recommendation"); + expect(match, "Expected improvements for ? key existence query").toBeTruthy(); + expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); - const ginRecs = getGinRecommendations(match); + const ginRecs = getGinRecommendations(match!); // ? requires jsonb_ops — must NOT have jsonb_path_ops - assert( - ginRecs.every((r) => - !r.definition.toLowerCase().includes("jsonb_path_ops") - ), - `Expected default jsonb_ops (no jsonb_path_ops) for ? operator, got: ${ - ginRecs.map((r) => r.definition) - }`, - ); + expect( + ginRecs.every((r) => !r.definition.toLowerCase().includes("jsonb_path_ops")), + `Expected default jsonb_ops (no jsonb_path_ops) for ? operator, got: ${ginRecs.map((r) => r.definition)}`, + ).toBeTruthy(); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── // 3. Any-key existence ?| → GIN default jsonb_ops // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: any-key existence (?|) recommends GIN with default jsonb_ops", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: any-key existence (?|) recommends GIN with default jsonb_ops", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -283,33 +268,26 @@ Deno.test({ const match = improvements.find((q) => q.query.includes("?|") || q.query.includes("payload") ); - assert(match, "Expected improvements for ?| any-key existence query"); - assert(hasGinRecommendation(match), "Expected a GIN index recommendation"); - - const ginRecs = getGinRecommendations(match); - assert( - ginRecs.every((r) => - !r.definition.toLowerCase().includes("jsonb_path_ops") - ), - `Expected default jsonb_ops for ?| operator, got: ${ - ginRecs.map((r) => r.definition) - }`, - ); + expect(match, "Expected improvements for ?| any-key existence query").toBeTruthy(); + expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + + const ginRecs = getGinRecommendations(match!); + expect( + ginRecs.every((r) => !r.definition.toLowerCase().includes("jsonb_path_ops")), + `Expected default jsonb_ops for ?| operator, got: ${ginRecs.map((r) => r.definition)}`, + ).toBeTruthy(); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── // 4. All-keys existence ?& → GIN default jsonb_ops // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: all-keys existence (?&) recommends GIN with default jsonb_ops", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: all-keys existence (?&) recommends GIN with default jsonb_ops", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -374,33 +352,26 @@ Deno.test({ const match = improvements.find((q) => q.query.includes("?&") || q.query.includes("payload") ); - assert(match, "Expected improvements for ?& all-keys existence query"); - assert(hasGinRecommendation(match), "Expected a GIN index recommendation"); - - const ginRecs = getGinRecommendations(match); - assert( - ginRecs.every((r) => - !r.definition.toLowerCase().includes("jsonb_path_ops") - ), - `Expected default jsonb_ops for ?& operator, got: ${ - ginRecs.map((r) => r.definition) - }`, - ); + expect(match, "Expected improvements for ?& all-keys existence query").toBeTruthy(); + expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + + const ginRecs = getGinRecommendations(match!); + expect( + ginRecs.every((r) => !r.definition.toLowerCase().includes("jsonb_path_ops")), + `Expected default jsonb_ops for ?& operator, got: ${ginRecs.map((r) => r.definition)}`, + ).toBeTruthy(); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── // 5. Mixed JSONB + regular column → GIN + B-tree // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: mixed JSONB and regular column produces both GIN and B-tree", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: mixed JSONB and regular column produces both GIN and B-tree", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -468,41 +439,40 @@ Deno.test({ const match = improvements.find((q) => q.query.includes("products") ); - assert(match, "Expected improvements for mixed JSONB + regular query"); - assert( - match.optimization.state === "improvements_available", - `Expected improvements_available but got ${match.optimization.state}`, - ); + expect(match, "Expected improvements for mixed JSONB + regular query").toBeTruthy(); + expect( + match!.optimization.state, + `Expected improvements_available but got ${match!.optimization.state}`, + ).toEqual("improvements_available"); - const ginRecs = getGinRecommendations(match); - const btreeRecs = getBtreeRecommendations(match); + const ginRecs = getGinRecommendations(match!); + const btreeRecs = getBtreeRecommendations(match!); // Should have a GIN recommendation for the JSONB column - assert( - ginRecs.length > 0, - `Expected GIN recommendation for data column, got: ${ - JSON.stringify(match.optimization.indexRecommendations.map((r) => r.definition)) - }`, - ); + expect( + ginRecs.length, + `Expected GIN recommendation for data column, got: ${JSON.stringify(match!.optimization.state === "improvements_available" ? match!.optimization.indexRecommendations.map((r) => r.definition) : [])}`, + ).toBeGreaterThan(0); // The two index types should not interfere — GIN for data, B-tree for price // The optimizer may or may not also produce a B-tree for price depending on // cost analysis, but the GIN and B-tree candidates must remain separate types for (const gin of ginRecs) { - assert( - !gin.definition.toLowerCase().includes("price"), + expect( + gin.definition.toLowerCase(), `GIN recommendation should not include non-JSONB column "price", got: ${gin.definition}`, - ); + ).not.toContain("price"); } for (const btree of btreeRecs) { - assert( - !btree.definition.toLowerCase().includes("using gin"), + expect( + btree.definition.toLowerCase(), `B-tree recommendation should not be a GIN index, got: ${btree.definition}`, - ); + ).not.toContain("using gin"); } } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── @@ -510,11 +480,7 @@ Deno.test({ // with default jsonb_ops (opclass escalation) // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: mixed @> and ? on same column escalates to jsonb_ops", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: mixed @> and ? on same column escalates to jsonb_ops", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -580,38 +546,32 @@ Deno.test({ const match = improvements.find((q) => q.query.includes("products") ); - assert(match, "Expected improvements for mixed @> and ? query"); + expect(match, "Expected improvements for mixed @> and ? query").toBeTruthy(); - const ginRecs = getGinRecommendations(match); + const ginRecs = getGinRecommendations(match!); // Should produce exactly ONE GIN index, not two - assertEquals( + expect( ginRecs.length, - 1, - `Expected exactly 1 merged GIN recommendation, got ${ginRecs.length}: ${ - ginRecs.map((r) => r.definition) - }`, - ); + `Expected exactly 1 merged GIN recommendation, got ${ginRecs.length}: ${ginRecs.map((r) => r.definition)}`, + ).toEqual(1); // Should use default jsonb_ops (no jsonb_path_ops) because ? requires it - assert( - !ginRecs[0].definition.toLowerCase().includes("jsonb_path_ops"), + expect( + ginRecs[0].definition.toLowerCase(), `Expected default jsonb_ops due to ? operator, got: ${ginRecs[0].definition}`, - ); + ).not.toContain("jsonb_path_ops"); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── // 7. Table-aliased JSONB column resolves correctly // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: table alias resolves to correct table for GIN recommendation", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: table alias resolves to correct table for GIN recommendation", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -676,32 +636,27 @@ Deno.test({ const match = improvements.find((q) => q.query.includes("products") ); - assert(match, "Expected improvements for aliased JSONB query"); - assert(hasGinRecommendation(match), "Expected a GIN index recommendation"); + expect(match, "Expected improvements for aliased JSONB query").toBeTruthy(); + expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); - const ginRecs = getGinRecommendations(match); + const ginRecs = getGinRecommendations(match!); // Should target the real table "products", not the alias "p" - assert( + expect( ginRecs.some((r) => r.table === "products"), - `Expected GIN recommendation on table "products", got: ${ - ginRecs.map((r) => `${r.table}: ${r.definition}`) - }`, - ); + `Expected GIN recommendation on table "products", got: ${ginRecs.map((r) => `${r.table}: ${r.definition}`)}`, + ).toBeTruthy(); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── // 8. Non-JSONB query → normal B-tree, no GIN // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: non-JSONB query produces B-tree only, no GIN", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: non-JSONB query produces B-tree only, no GIN", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -763,38 +718,31 @@ Deno.test({ await optimizer.finish; const match = improvements.find((q) => q.query.includes("users")); - assert(match, "Expected improvements for non-JSONB equality query"); + expect(match, "Expected improvements for non-JSONB equality query").toBeTruthy(); - const ginRecs = getGinRecommendations(match); - assertEquals( + const ginRecs = getGinRecommendations(match!); + expect( ginRecs.length, - 0, - `Expected no GIN recommendations for non-JSONB query, got: ${ - ginRecs.map((r) => r.definition) - }`, - ); + `Expected no GIN recommendations for non-JSONB query, got: ${ginRecs.map((r) => r.definition)}`, + ).toEqual(0); - const btreeRecs = getBtreeRecommendations(match); - assertGreater( + const btreeRecs = getBtreeRecommendations(match!); + expect( btreeRecs.length, - 0, "Expected B-tree recommendation for text equality query", - ); + ).toBeGreaterThan(0); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); // ────────────────────────────────────────────── // 9. Existing GIN index prevents duplicate // ────────────────────────────────────────────── -Deno.test({ - name: "GIN: existing GIN index prevents duplicate recommendation", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("GIN: existing GIN index prevents duplicate recommendation", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -870,18 +818,17 @@ Deno.test({ const ginImprovement = improvements.find((q) => q.query.includes("products") && hasGinRecommendation(q) ); - assert( - !ginImprovement, + expect( + ginImprovement, `Expected no duplicate GIN recommendation when one already exists, but got: ${ ginImprovement - ? JSON.stringify( - getGinRecommendations(ginImprovement).map((r) => r.definition), - ) + ? JSON.stringify(getGinRecommendations(ginImprovement).map((r) => r.definition)) : "none" }`, - ); + ).toBeFalsy(); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); diff --git a/src/remote/query-loader.test.ts b/src/remote/query-loader.test.ts index 72b0ad0..7751057 100644 --- a/src/remote/query-loader.test.ts +++ b/src/remote/query-loader.test.ts @@ -1,7 +1,4 @@ -import { assertEquals } from "@std/assert/equals"; -import { assertGreater } from "@std/assert/greater"; -import { stub } from "@std/testing/mock"; -import { FakeTime } from "@std/testing/time"; +import { test, expect, vi, afterEach } from "vitest"; import type { OptimizedQuery, QueryHash, @@ -13,6 +10,11 @@ import { QueryLoader } from "./query-loader.ts"; import { PostgresConnector } from "../sync/pg-connector.ts"; import { PostgresError } from "../sync/errors.ts"; +afterEach(() => { + vi.restoreAllMocks(); + vi.useRealTimers(); +}); + function createMockRecentQuery(query: string): RecentQuery { return { query, @@ -38,259 +40,249 @@ function createMockRecentQuery(query: string): RecentQuery { } as RecentQuery; } -Deno.test({ - name: "QueryLoader - poll emits poll event with queries", - fn: async () => { - const mockQueries = [ - createMockRecentQuery("SELECT * FROM users"), - createMockRecentQuery("SELECT * FROM posts"), - ]; +function stubConnector(manager: ConnectionManager, impl: Partial) { + vi.spyOn(manager, "getConnectorFor").mockReturnValue(impl as PostgresConnector); +} - const manager = ConnectionManager.forLocalDatabase(); - const connectable = Connectable.fromString( - "postgres://localhost:5432/test", - ); +test("QueryLoader - poll emits poll event with queries", async () => { + const mockQueries = [ + createMockRecentQuery("SELECT * FROM users"), + createMockRecentQuery("SELECT * FROM posts"), + ]; - using _ = stub(manager, "getConnectorFor", () => ({ - getRecentQueries: () => Promise.resolve(mockQueries), - } as PostgresConnector)); + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); - const loader = new QueryLoader(manager, connectable, { maxErrors: 3 }); + stubConnector(manager, { + getRecentQueries: () => Promise.resolve(mockQueries), + }); - const pollEvents: RecentQuery[][] = []; - loader.on("poll", (queries) => { - pollEvents.push(queries); - }); + const loader = new QueryLoader(manager, connectable, { maxErrors: 3 }); - const shouldContinue = await loader.poll(); + const pollEvents: RecentQuery[][] = []; + loader.on("poll", (queries) => { + pollEvents.push(queries); + }); - assertEquals(shouldContinue, true); - assertEquals(pollEvents.length, 1); - assertEquals(pollEvents[0], mockQueries); - }, + const shouldContinue = await loader.poll(); + + expect(shouldContinue).toEqual(true); + expect(pollEvents.length).toEqual(1); + expect(pollEvents[0]).toEqual(mockQueries); }); -Deno.test({ - name: "QueryLoader - poll handles errors and emits pollError", - fn: async () => { - const testError = new PostgresError("Database connection failed"); - - const manager = ConnectionManager.forLocalDatabase(); - const connectable = Connectable.fromString( - "postgres://localhost:5432/test", - ); - - using _ = stub(manager, "getConnectorFor", () => ({ - getRecentQueries: (): Promise => { - throw testError; - }, - } as PostgresConnector)); - - const loader = new QueryLoader(manager, connectable, { maxErrors: 3 }); - - const pollErrors: unknown[] = []; - loader.on("pollError", (error) => { - pollErrors.push(error); - }); - - const shouldContinue1 = await loader.poll(); - assertEquals(shouldContinue1, true); - assertEquals(pollErrors.length, 1); - assertEquals(pollErrors[0], testError); - - const shouldContinue2 = await loader.poll(); - assertEquals(shouldContinue2, true); - assertEquals(pollErrors.length, 2); - - const shouldContinue3 = await loader.poll(); - assertEquals(shouldContinue3, true); - assertEquals(pollErrors.length, 3); - }, +test("QueryLoader - poll handles errors and emits pollError", async () => { + const testError = new PostgresError("Database connection failed"); + + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); + + stubConnector(manager, { + getRecentQueries: (): Promise => { + throw testError; + }, + }); + + const loader = new QueryLoader(manager, connectable, { maxErrors: 3 }); + + const pollErrors: unknown[] = []; + loader.on("pollError", (error) => { + pollErrors.push(error); + }); + + const shouldContinue1 = await loader.poll(); + expect(shouldContinue1).toEqual(true); + expect(pollErrors.length).toEqual(1); + expect(pollErrors[0]).toEqual(testError); + + const shouldContinue2 = await loader.poll(); + expect(shouldContinue2).toEqual(true); + expect(pollErrors.length).toEqual(2); + + const shouldContinue3 = await loader.poll(); + expect(shouldContinue3).toEqual(true); + expect(pollErrors.length).toEqual(3); }); -Deno.test({ - name: "QueryLoader - exits after maxErrors consecutive errors", - fn: async () => { - const manager = ConnectionManager.forLocalDatabase(); - const connectable = Connectable.fromString( - "postgres://localhost:5432/test", - ); - - using _ = stub(manager, "getConnectorFor", () => ({ - getRecentQueries: (): Promise => { - throw new Error("Connection error"); - }, - } as PostgresConnector)); - - const loader = new QueryLoader(manager, connectable, { maxErrors: 2 }); - - const exitEvents: number[] = []; - loader.on("exit", () => { - exitEvents.push(Date.now()); - }); - - const shouldContinue1 = await loader.poll(); - assertEquals(shouldContinue1, true); - assertEquals(exitEvents.length, 0); - - const shouldContinue2 = await loader.poll(); - assertEquals(shouldContinue2, true); - assertEquals(exitEvents.length, 0); - - const shouldContinue3 = await loader.poll(); - assertEquals(shouldContinue3, false); - assertEquals(exitEvents.length, 1); - }, +test("QueryLoader - exits after maxErrors consecutive errors", async () => { + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); + + stubConnector(manager, { + getRecentQueries: (): Promise => { + throw new Error("Connection error"); + }, + }); + + const loader = new QueryLoader(manager, connectable, { maxErrors: 2 }); + + const exitEvents: number[] = []; + loader.on("exit", () => { + exitEvents.push(Date.now()); + }); + + const shouldContinue1 = await loader.poll(); + expect(shouldContinue1).toEqual(true); + expect(exitEvents.length).toEqual(0); + + const shouldContinue2 = await loader.poll(); + expect(shouldContinue2).toEqual(true); + expect(exitEvents.length).toEqual(0); + + const shouldContinue3 = await loader.poll(); + expect(shouldContinue3).toEqual(false); + expect(exitEvents.length).toEqual(1); }); -Deno.test({ - name: "QueryLoader - error counter resets on successful poll", - fn: async () => { - let callCount = 0; - - const manager = ConnectionManager.forLocalDatabase(); - const connectable = Connectable.fromString( - "postgres://localhost:5432/test", - ); - - using _ = stub(manager, "getConnectorFor", () => ({ - getRecentQueries: (): Promise => { - callCount++; - if (callCount !== 3) { - throw new Error("Temporary error"); - } - return Promise.resolve([createMockRecentQuery("SELECT 1")]); - }, - } as PostgresConnector)); - - const loader = new QueryLoader(manager, connectable, { maxErrors: 2 }); - - const pollErrors: unknown[] = []; - const pollEvents: RecentQuery[][] = []; - loader.on("pollError", (error) => pollErrors.push(error)); - loader.on("poll", (queries) => pollEvents.push(queries)); - - await loader.poll(); - assertEquals(pollErrors.length, 1); - assertEquals(pollEvents.length, 0); - - await loader.poll(); - assertEquals(pollErrors.length, 2); - assertEquals(pollEvents.length, 0); - - const shouldContinue = await loader.poll(); - assertEquals(shouldContinue, true); - assertEquals(pollErrors.length, 2); - assertEquals(pollEvents.length, 1); - - await loader.poll(); - await loader.poll(); - const finalResult = await loader.poll(); - assertEquals(finalResult, false); - }, +test("QueryLoader - error counter resets on successful poll", async () => { + let callCount = 0; + + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); + + stubConnector(manager, { + getRecentQueries: (): Promise => { + callCount++; + if (callCount !== 3) { + throw new Error("Temporary error"); + } + return Promise.resolve([createMockRecentQuery("SELECT 1")]); + }, + }); + + const loader = new QueryLoader(manager, connectable, { maxErrors: 2 }); + + const pollErrors: unknown[] = []; + const pollEvents: RecentQuery[][] = []; + loader.on("pollError", (error) => pollErrors.push(error)); + loader.on("poll", (queries) => pollEvents.push(queries)); + + await loader.poll(); + expect(pollErrors.length).toEqual(1); + expect(pollEvents.length).toEqual(0); + + await loader.poll(); + expect(pollErrors.length).toEqual(2); + expect(pollEvents.length).toEqual(0); + + const shouldContinue = await loader.poll(); + expect(shouldContinue).toEqual(true); + expect(pollErrors.length).toEqual(2); + expect(pollEvents.length).toEqual(1); + + await loader.poll(); + await loader.poll(); + const finalResult = await loader.poll(); + expect(finalResult).toEqual(false); }); -Deno.test({ - name: "QueryLoader - stop prevents further polling", - fn: async () => { - using time = new FakeTime(); +test("QueryLoader - stop prevents further polling", async () => { + vi.useFakeTimers(); - const mockQueries = [createMockRecentQuery("SELECT 1")]; + const mockQueries = [createMockRecentQuery("SELECT 1")]; - const manager = ConnectionManager.forLocalDatabase(); - const connectable = Connectable.fromString( - "postgres://localhost:5432/test", - ); + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); - using _ = stub(manager, "getConnectorFor", () => ({ - getRecentQueries: () => Promise.resolve(mockQueries), - } as PostgresConnector)); + stubConnector(manager, { + getRecentQueries: () => Promise.resolve(mockQueries), + }); - const loader = new QueryLoader(manager, connectable); + const loader = new QueryLoader(manager, connectable); - let pollCount = 0; - loader.on("poll", () => { - pollCount++; - }); + let pollCount = 0; + loader.on("poll", () => { + pollCount++; + }); - loader.start(); - loader.stop(); + loader.start(); + loader.stop(); - await time.tickAsync(10000); + await vi.advanceTimersByTimeAsync(10000); - assertEquals(pollCount, 0); - }, + expect(pollCount).toEqual(0); }); -Deno.test({ - name: "QueryLoader - start schedules polls with default interval", - fn: async () => { - using time = new FakeTime(); +test("QueryLoader - start schedules polls with default interval", async () => { + vi.useFakeTimers(); - const mockQueries = [createMockRecentQuery("SELECT 1")]; + const mockQueries = [createMockRecentQuery("SELECT 1")]; - const manager = ConnectionManager.forLocalDatabase(); - const connectable = Connectable.fromString( - "postgres://localhost:5432/test", - ); + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); - using _ = stub(manager, "getConnectorFor", () => ({ - getRecentQueries: () => Promise.resolve(mockQueries), - } as PostgresConnector)); + stubConnector(manager, { + getRecentQueries: () => Promise.resolve(mockQueries), + }); - const loader = new QueryLoader(manager, connectable); + const loader = new QueryLoader(manager, connectable); - let pollCount = 0; - loader.on("poll", () => { - pollCount++; - }); + let pollCount = 0; + loader.on("poll", () => { + pollCount++; + }); - loader.start(); + loader.start(); - await time.tickAsync(10000); + await vi.advanceTimersByTimeAsync(10000); - assertGreater(pollCount, 0); + expect(pollCount).toBeGreaterThan(0); - loader.stop(); - }, + loader.stop(); }); -Deno.test({ - name: - "QueryLoader - emits exit on unexpected promise rejection in scheduled poll", - fn: async () => { - using time = new FakeTime(); +test("QueryLoader - handles non-Error exceptions", async () => { + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); + + stubConnector(manager, { + getRecentQueries: (): Promise => { + throw "String error"; + }, + }); - let shouldFail = false; + const loader = new QueryLoader(manager, connectable, { maxErrors: 1 }); - const manager = ConnectionManager.forLocalDatabase(); - const connectable = Connectable.fromString( - "postgres://localhost:5432/test", - ); + const pollErrors: unknown[] = []; + loader.on("pollError", (error) => { + pollErrors.push(error); + }); + + const shouldContinue = await loader.poll(); + expect(shouldContinue).toEqual(true); + expect(pollErrors.length).toEqual(1); +}); - using _ = stub(manager, "getConnectorFor", () => ({ - getRecentQueries: (): Promise => { - if (shouldFail) { - throw new PostgresError("String error"); - } - return Promise.resolve([createMockRecentQuery("SELECT 1")]); - }, - } as PostgresConnector)); +test("QueryLoader - emits exit on unexpected promise rejection in scheduled poll", async () => { + vi.useFakeTimers(); + + let shouldFail = false; + + const manager = ConnectionManager.forLocalDatabase(); + const connectable = Connectable.fromString("postgres://localhost:5432/test"); + + stubConnector(manager, { + getRecentQueries: (): Promise => { + if (shouldFail) { + throw new PostgresError("Unexpected error"); + } + return Promise.resolve([createMockRecentQuery("SELECT 1")]); + }, + }); - const loader = new QueryLoader(manager, connectable, { maxErrors: 0 }); + const loader = new QueryLoader(manager, connectable, { maxErrors: 0 }); - const exitEvents: number[] = []; - loader.on("exit", () => { - exitEvents.push(Date.now()); - }); + const exitEvents: number[] = []; + loader.on("exit", () => { + exitEvents.push(Date.now()); + }); - shouldFail = true; - loader.start(); + shouldFail = true; + loader.start(); - await time.tickAsync(10000); - await time.runMicrotasks(); + await vi.advanceTimersByTimeAsync(10000); + await vi.advanceTimersByTimeAsync(0); - assertGreater(exitEvents.length, 0); - loader.stop(); - }, + expect(exitEvents.length).toBeGreaterThan(0); + loader.stop(); }); diff --git a/src/remote/query-optimizer.test.ts b/src/remote/query-optimizer.test.ts index 8d0844d..5ac2687 100644 --- a/src/remote/query-optimizer.test.ts +++ b/src/remote/query-optimizer.test.ts @@ -1,17 +1,13 @@ +import { test, expect, vi, afterEach } from "vitest"; import { PostgreSqlContainer } from "@testcontainers/postgresql"; import { QueryOptimizer } from "./query-optimizer.ts"; import { ConnectionManager } from "../sync/connection-manager.ts"; import { Connectable } from "../sync/connectable.ts"; import { setTimeout } from "node:timers/promises"; -import { assertArrayIncludes } from "@std/assert/array-includes"; -import { assert, assertEquals, assertGreater } from "@std/assert"; + import { type OptimizedQuery, RecentQuery } from "../sql/recent-query.ts"; -Deno.test({ - name: "controller syncs correctly", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("controller syncs correctly", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -100,21 +96,21 @@ Deno.test({ }], }); // should ignore the query with - assert( + expect( includedQueries.every((q) => !q.query.startsWith("select * from pg_class where relname > $1") ), "Optimizer did not ignore a query with @qd_introspection", - ); - assert( + ).toBeTruthy(); + expect( includedQueries.every((q) => !q.query.startsWith("select * from pg_index where $1 = $2") ), "Optimizer did not ignore a system query", - ); + ).toBeTruthy(); await setTimeout(1_000); - assertArrayIncludes(expectedImprovements, improvements); - assertArrayIncludes(expectedNoImprovements, noImprovements); + expect(expectedImprovements).toEqual(expect.arrayContaining(improvements)); + expect(expectedNoImprovements).toEqual(expect.arrayContaining(noImprovements)); improvements = []; noImprovements = []; await optimizer.addQueries([ @@ -142,11 +138,10 @@ Deno.test({ 1, ), ]); - assertArrayIncludes( + expect( [...expectedImprovements, "select * from testing where a >= $1;"], - improvements, - ); - assertArrayIncludes(expectedNoImprovements, noImprovements); + ).toEqual(expect.arrayContaining(improvements)); + expect(expectedNoImprovements).toEqual(expect.arrayContaining(noImprovements)); console.log("improvements 1", improvements); console.log("no improvements 1", noImprovements); await optimizer.start(recentQueries, { @@ -177,16 +172,13 @@ Deno.test({ console.log("improvements", improvements); console.log("no improvements", noImprovements); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); -Deno.test({ - name: "disabling an index removes it from indexesUsed and recommends it", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("disabling an index removes it from indexesUsed and recommends it", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -247,86 +239,68 @@ Deno.test({ const emailQuery = recentQueries.find((q) => q.query.includes("email") && q.query.includes("users") ); - assert(emailQuery, "Expected to find email query in recent queries"); + expect(emailQuery, "Expected to find email query in recent queries").toBeTruthy(); - await optimizer.start([emailQuery], statsMode); + await optimizer.start([emailQuery!], statsMode); await optimizer.finish; const queriesAfterFirstRun = optimizer.getQueries(); const emailQueryResult = queriesAfterFirstRun.find((q) => q.query.includes("email") ); - assert(emailQueryResult, "Expected email query in results"); - assert( - emailQueryResult.optimization.state === "no_improvement_found", - `Expected no_improvement_found but got ${emailQueryResult.optimization.state}`, - ); - assertArrayIncludes( - emailQueryResult.optimization.indexesUsed, - ["users_email_idx"], - ); - const costWithIndex = emailQueryResult.optimization.cost; + expect(emailQueryResult, "Expected email query in results").toBeTruthy(); + expect( + emailQueryResult!.optimization.state, + `Expected no_improvement_found but got ${emailQueryResult!.optimization.state}`, + ).toEqual("no_improvement_found"); + expect((emailQueryResult!.optimization as any).indexesUsed).toEqual(expect.arrayContaining(["users_email_idx"])); + const costWithIndex = (emailQueryResult!.optimization as any).cost; const { PgIdentifier } = await import("@query-doctor/core"); optimizer.toggleIndex(PgIdentifier.fromString("users_email_idx")); const disabledIndexes = optimizer.getDisabledIndexes(); - assert( + expect( disabledIndexes.some((i) => i.toString() === "users_email_idx"), - `Expected users_email_idx to be disabled`, - ); + "Expected users_email_idx to be disabled", + ).toBeTruthy(); - await optimizer.addQueries([emailQuery]); + await optimizer.addQueries([emailQuery!]); await optimizer.finish; const queriesAfterToggle = optimizer.getQueries(); const emailQueryAfterToggle = queriesAfterToggle.find((q) => q.query.includes("email") ); - assert(emailQueryAfterToggle, "Expected email query after toggle"); - assert( - emailQueryAfterToggle.optimization.state === "improvements_available", - `Expected improvements_available after toggle but got ${emailQueryAfterToggle.optimization.state}`, - ); - assert( - !emailQueryAfterToggle.optimization.indexesUsed.includes( - "users_email_idx", - ), - "Expected users_email_idx to NOT be in indexesUsed after disabling", - ); - assertGreater( - emailQueryAfterToggle.optimization.cost, - costWithIndex, - "Expected cost without index to be higher than cost with index", - ); + expect(emailQueryAfterToggle, "Expected email query after toggle").toBeTruthy(); + expect( + emailQueryAfterToggle!.optimization.state, + `Expected improvements_available after toggle but got ${emailQueryAfterToggle!.optimization.state}`, + ).toEqual("improvements_available"); + expect((emailQueryAfterToggle!.optimization as any).indexesUsed).not.toContain("users_email_idx"); + expect((emailQueryAfterToggle!.optimization as any).cost).toBeGreaterThan(costWithIndex); const recommendations = - emailQueryAfterToggle.optimization.indexRecommendations; - assert( - recommendations.some((r) => - r.columns.some((c) => c.column === "email") + (emailQueryAfterToggle!.optimization as any).indexRecommendations; + expect( + recommendations.some((r: any) => + r.columns.some((c: any) => c.column === "email") ), "Expected recommendation for email column after disabling the index", - ); + ).toBeTruthy(); // Verify explainPlan doesn't show the disabled index being used const explainPlanAfterToggle = - emailQueryAfterToggle.optimization.explainPlan; - assert(explainPlanAfterToggle, "Expected explainPlan to be present"); + (emailQueryAfterToggle!.optimization as any).explainPlan; + expect(explainPlanAfterToggle, "Expected explainPlan to be present").toBeTruthy(); const explainStr = JSON.stringify(explainPlanAfterToggle); - assert( - !explainStr.includes("users_email_idx"), - `Expected explainPlan to NOT contain disabled index "users_email_idx" but found it in: ${explainStr}`, - ); + expect(explainStr).not.toContain("users_email_idx"); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); -Deno.test({ - name: "hypertable optimization includes index recommendations", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("hypertable optimization includes index recommendations", async () => { const pg = await new PostgreSqlContainer( "timescale/timescaledb:latest-pg16", ) @@ -444,25 +418,17 @@ Deno.test({ // The bug: when improvements_available, indexRecommendations should not be empty for (const q of improvementsWithRecommendations) { if (q.optimization.state === "improvements_available") { - assertGreater( - q.optimization.indexRecommendations.length, - 0, - `Query "${q.query}" has ${q.optimization.costReductionPercentage}% cost reduction but no index recommendations`, - ); + expect(q.optimization.indexRecommendations.length).toBeGreaterThan(0); } } } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); -Deno.test({ - name: - "timed out queries are retried with exponential backoff up to maxRetries", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("timed out queries are retried with exponential backoff up to maxRetries", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -511,9 +477,9 @@ Deno.test({ const slowQuery = recentQueries.find((q) => q.query.includes("slow_table") && q.query.startsWith("select") ); - assert(slowQuery, "Expected to find slow_table query"); + expect(slowQuery, "Expected to find slow_table query").toBeTruthy(); - await optimizer.start([slowQuery], { + await optimizer.start([slowQuery!], { kind: "fromStatisticsExport", source: { kind: "inline" }, stats: [{ @@ -534,32 +500,27 @@ Deno.test({ const queries = optimizer.getQueries(); const resultQuery = queries.find((q) => q.query.includes("slow_table")); - assert(resultQuery, "Expected slow_table query in results"); + expect(resultQuery, "Expected slow_table query in results").toBeTruthy(); - assertEquals( - resultQuery.optimization.state, - "timeout", + expect( + resultQuery!.optimization.state, "Expected query to be in timeout state", - ); + ).toEqual("timeout"); - if (resultQuery.optimization.state === "timeout") { - assertEquals( - resultQuery.optimization.retries, - maxRetries, + if (resultQuery!.optimization.state === "timeout") { + expect( + resultQuery!.optimization.retries, `Expected ${maxRetries} retries`, - ); + ).toEqual(maxRetries); } } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); -Deno.test({ - name: "optimizer does not treat ASC index as duplicate of DESC candidate", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("optimizer does not treat ASC index as duplicate of DESC candidate", async () => { const pg = await new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ { @@ -603,9 +564,9 @@ Deno.test({ const mixedQuery = recentQueries.find((q) => q.query.includes("order by created_at desc, status asc") ); - assert(mixedQuery, "Expected to find mixed sort direction query"); + expect(mixedQuery, "Expected to find mixed sort direction query").toBeTruthy(); - await optimizer.start([mixedQuery], { + await optimizer.start([mixedQuery!], { kind: "fromStatisticsExport", source: { kind: "inline" }, stats: [{ @@ -634,24 +595,24 @@ Deno.test({ const result = queries.find((q) => q.query.includes("order by created_at desc, status asc") ); - assert(result, "Expected to find query result"); - assert( - result.optimization.state === "improvements_available", - `Expected improvements_available (ASC,ASC can't satisfy DESC,ASC via backward scan). ` + - `Got: ${result.optimization.state}`, - ); - - const recommendations = result.optimization.indexRecommendations; - const hasMixedRecommendation = recommendations.some((r) => + expect(result, "Expected to find query result").toBeTruthy(); + expect( + result!.optimization.state, + `Expected improvements_available (ASC,ASC can't satisfy DESC,ASC via backward scan). Got: ${result!.optimization.state}`, + ).toEqual("improvements_available"); + + const recommendations = (result!.optimization as any).indexRecommendations; + const hasMixedRecommendation = recommendations.some((r: any) => r.columns.length >= 2 && - r.columns.some((c) => c.column === "created_at" && c.sort?.dir === "SORTBY_DESC") + r.columns.some((c: any) => c.column === "created_at" && c.sort?.dir === "SORTBY_DESC") ); - assert( + expect( hasMixedRecommendation, `Expected recommendation with created_at DESC. Got: ${JSON.stringify(recommendations)}`, - ); + ).toBeTruthy(); } finally { + optimizer.stop(); + await manager.closeAll(); await pg.stop(); } - }, }); diff --git a/src/remote/query-optimizer.ts b/src/remote/query-optimizer.ts index e3e4cb8..fd299b7 100644 --- a/src/remote/query-optimizer.ts +++ b/src/remote/query-optimizer.ts @@ -51,7 +51,7 @@ export class QueryOptimizer extends EventEmitter { private target?: Target; private semaphore = new Sema(QueryOptimizer.MAX_CONCURRENCY); - private _finish = Promise.withResolvers(); + private _finish = Promise.withResolvers(); private _validQueriesProcessed = 0; private _invalidQueries = 0; @@ -140,7 +140,7 @@ export class QueryOptimizer extends EventEmitter { this.semaphore = new Sema(QueryOptimizer.MAX_CONCURRENCY); this.queries.clear(); this.target = undefined; - this._finish = Promise.withResolvers(); + this._finish = Promise.withResolvers(); this._allQueries = 0; this._invalidQueries = 0; this._validQueriesProcessed = 0; @@ -153,7 +153,7 @@ export class QueryOptimizer extends EventEmitter { } else { this.resetQueryOptimizationState(); } - this._finish = Promise.withResolvers(); + this._finish = Promise.withResolvers(); this._invalidQueries = 0; this._validQueriesProcessed = 0; if (this.target) { @@ -281,7 +281,7 @@ export class QueryOptimizer extends EventEmitter { this.semaphore.release(token); } if (!optimized) { - this._finish.resolve(0); + this._finish.resolve(); break; } this._validQueriesProcessed++; diff --git a/src/remote/remote-controller.test.ts b/src/remote/remote-controller.test.ts index 746f88d..e7cbc3e 100644 --- a/src/remote/remote-controller.test.ts +++ b/src/remote/remote-controller.test.ts @@ -1,21 +1,14 @@ +import { test, expect, vi } from "vitest"; import { PostgreSqlContainer } from "@testcontainers/postgresql"; import { Connectable } from "../sync/connectable.ts"; import { Remote } from "./remote.ts"; -import postgres from "postgresjs"; -import { assertEquals } from "@std/assert/equals"; +import { Pool } from "pg"; + import { RemoteController } from "./remote-controller.ts"; import { ConnectionManager } from "../sync/connection-manager.ts"; import { RemoteSyncRequest } from "./remote.dto.ts"; -import { spy } from "@std/testing/mock"; -import { setTimeout } from "node:timers/promises"; -import { assertGreaterOrEqual } from "@std/assert"; -Deno.test({ - name: "controller syncs correctly", - sanitizeOps: false, - // deno is weird... the sync seems like it might be leaking resources? - sanitizeResources: false, - fn: async () => { +test("controller syncs correctly", async () => { const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ @@ -34,77 +27,42 @@ Deno.test({ .start(), new PostgreSqlContainer("postgres:17").start(), ]); - const controller = new AbortController(); - - const target = Connectable.fromString( - targetDb.getConnectionUri(), - ); - const source = Connectable.fromString( - sourceDb.getConnectionUri(), - ); + const target = Connectable.fromString(targetDb.getConnectionUri()); + const source = Connectable.fromString(sourceDb.getConnectionUri()); const sourceOptimizer = ConnectionManager.forLocalDatabase(); - const remote = new RemoteController( - new Remote(target, sourceOptimizer), - ); + const innerRemote = new Remote(target, sourceOptimizer); + const remote = new RemoteController(innerRemote); - const server = Deno.serve( - { port: 0, signal: controller.signal }, - async (req: Request): Promise => { - const result = await remote.execute(req); - if (!result) { - throw new Error(); - } - return result; - }, - ); try { - const ws = new WebSocket(`ws://localhost:${server.addr.port}/postgres`); - const messageFunction = spy(); - ws.addEventListener("error", console.error); - ws.addEventListener("message", messageFunction); - - const response = await fetch( - new Request( - `http://localhost:${server.addr.port}/postgres`, - { - method: "POST", - body: RemoteSyncRequest.encode({ - db: source, - }), - }, - ), + const syncResult = await remote.onFullSync( + RemoteSyncRequest.encode({ db: source }), ); - assertEquals(response?.status, 200); - await setTimeout(1000); + expect(syncResult.status).toEqual(200); - const sql = postgres( - target.withDatabaseName(Remote.optimizingDbName).toString(), - ); + const pool = new Pool({ + connectionString: target.withDatabaseName(Remote.optimizingDbName).toString(), + }); const tablesAfter = - await sql`select tablename from pg_tables where schemaname = 'public'`; - assertEquals(tablesAfter.count, 1); + await pool.query("select tablename from pg_tables where schemaname = 'public'"); + expect(tablesAfter.rowCount).toEqual(1); const indexesAfter = - await sql`select * from pg_indexes where schemaname = 'public'`; - assertEquals(indexesAfter.count, 1); - assertEquals(tablesAfter[0], { tablename: "testing" }); - const rows = await sql`select * from testing`; - assertEquals(rows.length, 0); - - assertGreaterOrEqual(messageFunction.calls.length, 1); + await pool.query("select * from pg_indexes where schemaname = 'public'"); + expect(indexesAfter.rowCount).toEqual(1); + expect(tablesAfter.rows[0]).toEqual({ tablename: "testing" }); + const rows = await pool.query("select * from testing"); + expect(rows.rowCount).toEqual(0); + + await pool.end(); + await innerRemote.cleanup(); } finally { - await Promise.all([sourceDb.stop(), targetDb.stop(), server.shutdown()]); + await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); -Deno.test({ - name: "creating an index via endpoint adds it to the optimizing db", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("creating an index via endpoint adds it to the optimizing db", async () => { const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ @@ -121,81 +79,54 @@ Deno.test({ .start(), new PostgreSqlContainer("postgres:17").start(), ]); - const controller = new AbortController(); const target = Connectable.fromString(targetDb.getConnectionUri()); const source = Connectable.fromString(sourceDb.getConnectionUri()); const sourceOptimizer = ConnectionManager.forLocalDatabase(); - const remote = new RemoteController( - new Remote(target, sourceOptimizer), - ); - - const server = Deno.serve( - { port: 0, signal: controller.signal }, - async (req: Request): Promise => { - const result = await remote.execute(req); - if (!result) { - return new Response("Not found", { status: 404 }); - } - return result; - }, - ); + const innerRemote = new Remote(target, sourceOptimizer); + const remote = new RemoteController(innerRemote); try { // First sync the database - const syncResponse = await fetch( - `http://localhost:${server.addr.port}/postgres`, - { - method: "POST", - body: RemoteSyncRequest.encode({ db: source }), - }, + const syncResult = await remote.onFullSync( + RemoteSyncRequest.encode({ db: source }), ); - assertEquals(syncResponse.status, 200); + expect(syncResult.status).toEqual(200); - const sql = postgres( - target.withDatabaseName(Remote.optimizingDbName).toString(), - ); + const pool = new Pool({ + connectionString: target.withDatabaseName(Remote.optimizingDbName).toString(), + }); // Verify no indexes exist initially const indexesBefore = - await sql`select * from pg_indexes where schemaname = 'public'`; - assertEquals(indexesBefore.count, 0); + await pool.query("select * from pg_indexes where schemaname = 'public'"); + expect(indexesBefore.rowCount).toEqual(0); - // Create an index via the endpoint - const createResponse = await fetch( - `http://localhost:${server.addr.port}/postgres/indexes`, - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - connectionString: sourceDb.getConnectionUri(), - table: "testing", - columns: [{ name: "a", order: "asc" }], - }), - }, - ); + // Create an index via the controller method + const createResult = await remote.createIndex({ + connectionString: sourceDb.getConnectionUri(), + table: "testing", + columns: [{ name: "a", order: "asc" }], + }); - assertEquals(createResponse.status, 200); - const body = await createResponse.json(); - assertEquals(body.success, true); + expect(createResult.status).toEqual(200); + expect((createResult.body as any).success).toEqual(true); // Verify the index was created on the optimizing db const indexesAfter = - await sql`select * from pg_indexes where schemaname = 'public'`; - assertEquals(indexesAfter.count, 1); - assertEquals(indexesAfter[0].tablename, "testing"); + await pool.query("select * from pg_indexes where schemaname = 'public'"); + expect(indexesAfter.rowCount).toEqual(1); + expect(indexesAfter.rows[0].tablename).toEqual("testing"); + + await pool.end(); + await innerRemote.cleanup(); } finally { - await Promise.all([sourceDb.stop(), targetDb.stop(), server.shutdown()]); + await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); -Deno.test({ - name: "controller returns extension error when pg_stat_statements is not installed", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("controller returns extension error when pg_stat_statements is not installed", async () => { const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ @@ -211,55 +142,30 @@ Deno.test({ .start(), new PostgreSqlContainer("postgres:17").start(), ]); - const controller = new AbortController(); - - const target = Connectable.fromString( - targetDb.getConnectionUri(), - ); - const source = Connectable.fromString( - sourceDb.getConnectionUri(), - ); + const target = Connectable.fromString(targetDb.getConnectionUri()); + const source = Connectable.fromString(sourceDb.getConnectionUri()); const sourceOptimizer = ConnectionManager.forLocalDatabase(); - const remoteInstance = new Remote(target, sourceOptimizer); - const remote = new RemoteController(remoteInstance); + const innerRemote = new Remote(target, sourceOptimizer); + const remote = new RemoteController(innerRemote); - const server = Deno.serve( - { port: 0, signal: controller.signal }, - async (req: Request): Promise => { - const result = await remote.execute(req); - if (!result) { - throw new Error(); - } - return result; - }, - ); try { - const response = await fetch( - new Request( - `http://localhost:${server.addr.port}/postgres`, - { - method: "POST", - body: RemoteSyncRequest.encode({ - db: source, - }), - }, - ), + const syncResult = await remote.onFullSync( + RemoteSyncRequest.encode({ db: source }), ); - assertEquals(response.status, 200); - const data = await response.json(); + expect(syncResult.status).toEqual(200); + const body = syncResult.body as any; // Schema should still sync successfully - assertEquals(data.schema.type, "ok"); + expect(body.schema.type).toEqual("ok"); // Queries should return the extension_not_installed error - assertEquals(data.queries.type, "error"); - assertEquals(data.queries.error, "extension_not_installed"); + expect(body.queries.type).toEqual("error"); + expect(body.queries.error).toEqual("extension_not_installed"); } finally { - await remoteInstance.cleanup(); - await Promise.all([sourceDb.stop(), targetDb.stop(), server.shutdown()]); + await innerRemote.cleanup(); + await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); diff --git a/src/remote/remote-controller.ts b/src/remote/remote-controller.ts index 1247db3..cb8819c 100644 --- a/src/remote/remote-controller.ts +++ b/src/remote/remote-controller.ts @@ -1,3 +1,4 @@ +import type { WebSocket } from "ws"; import { env } from "../env.ts"; import { log } from "../log.ts"; import { RemoteSyncRequest } from "./remote.dto.ts"; @@ -19,6 +20,11 @@ const SyncStatus = { type SyncStatus = typeof SyncStatus[keyof typeof SyncStatus]; +type HandlerResult = { + status: number; + body: unknown; +}; + export class RemoteController { /** * Only a single socket can be active at the same time. @@ -34,46 +40,6 @@ export class RemoteController { this.hookUpWebsockets(remote); } - async execute( - request: Request, - ): Promise { - const url = new URL(request.url); - - if (url.pathname === "/postgres") { - const isWebsocket = request.headers.get("upgrade") === "websocket"; - if (isWebsocket) { - return this.onWebsocketRequest(request); - } else if (request.method === "POST") { - return await this.onFullSync(request); - } else if (request.method === "GET") { - return await this.getStatus(); - } - return methodNotAllowed(); - } - - if (url.pathname === "/postgres/indexes/toggle") { - if (request.method === "POST") { - return await this.toggleIndex(request); - } - return methodNotAllowed(); - } - - if (url.pathname === "/postgres/reset") { - if (request.method === "POST") { - return await this.onReset(request); - } - return methodNotAllowed(); - } - - if (url.pathname === "/postgres/indexes") { - if (request.method === "POST") { - return await this.createIndex(request); - } - return methodNotAllowed(); - } - - } - private hookUpWebsockets(remote: Remote) { const onQueryProcessed = this.eventOnQueryProcessed.bind(this); const onError = this.eventError.bind(this); @@ -86,35 +52,36 @@ export class RemoteController { remote.on("restoreLog", this.makeLoggingHandler("pg_restore").bind(this)); } - private async toggleIndex(request: Request): Promise { + async toggleIndex(body: unknown): Promise { try { - const data = await request.json(); - const index = ToggleIndexDto.decode(data); + const index = ToggleIndexDto.parse(body); const isDisabled = this.remote.optimizer.toggleIndex(index.indexName); - return Response.json({ isDisabled }); + return { status: 200, body: { isDisabled } }; } catch (error) { if (error instanceof ZodError) { - return Response.json({ - type: "error", - error: "invalid_body", - message: error.message, - }, { + return { status: 400, - }); + body: { + type: "error", + error: "invalid_body", + message: error.message, + }, + }; } - return Response.json({ - type: "error", - error: env.HOSTED ? "Internal Server Error" : error, - message: "Failed to sync database", - }, { + return { status: 500, - }); + body: { + type: "error", + error: env.HOSTED ? "Internal Server Error" : error, + message: "Failed to sync database", + }, + }; } } - private async getStatus(): Promise { + async getStatus(): Promise { if (!this.syncResponse || this.syncStatus !== SyncStatus.COMPLETED) { - return Response.json({ status: this.syncStatus }); + return { status: this.syncStatus }; } const { schema, meta } = this.syncResponse; const { queries, diffs, disabledIndexes, pgStatStatementsNotInstalled } = @@ -126,7 +93,7 @@ export class RemoteController { } else { deltas = { type: "error", value: String(diffs.reason) }; } - return Response.json({ + return { status: this.syncStatus, meta, schema, @@ -135,7 +102,7 @@ export class RemoteController { : { type: "ok", value: queries }, disabledIndexes: { type: "ok", value: disabledIndexes }, deltas, - }); + }; } private pgStatStatementsNotInstalledError() { @@ -146,10 +113,10 @@ export class RemoteController { } as const; } - private async onFullSync(request: Request): Promise { - const body = RemoteSyncRequest.safeDecode(await request.text()); + async onFullSync(rawBody: string): Promise { + const body = RemoteSyncRequest.safeDecode(rawBody); if (!body.success) { - return new Response(JSON.stringify(body.error), { status: 400 }); + return { status: 400, body: body.error }; } const { db } = body.data; @@ -163,115 +130,125 @@ export class RemoteController { const { queries, pgStatStatementsNotInstalled } = await this.remote .getStatus(); - return Response.json({ - meta, - schema, - queries: pgStatStatementsNotInstalled - ? this.pgStatStatementsNotInstalledError() - : { type: "ok", value: queries }, - }); + return { + status: 200, + body: { + meta, + schema, + queries: pgStatStatementsNotInstalled + ? this.pgStatStatementsNotInstalledError() + : { type: "ok", value: queries }, + }, + }; } catch (error) { this.syncStatus = SyncStatus.FAILED; console.error(error); - return Response.json({ - type: "error", - error: env.HOSTED ? "Internal Server Error" : error, - message: "Failed to sync database", - }, { + return { status: 500, - }); + body: { + type: "error", + error: env.HOSTED ? "Internal Server Error" : error, + message: "Failed to sync database", + }, + }; } } - private async onReset(request: Request): Promise { - const body = RemoteSyncRequest.safeDecode(await request.text()); + async onReset(rawBody: string): Promise { + const body = RemoteSyncRequest.safeDecode(rawBody); if (!body.success) { - return new Response(JSON.stringify(body.error), { status: 400 }); + return { status: 400, body: body.error }; } try { await this.remote.resetPgStatStatements(body.data.db); - return Response.json({ success: true }); + return { status: 200, body: { success: true } }; } catch (error) { console.error(error); if (error instanceof errors.PostgresError) { - return error.toResponse(); + return { status: error.statusCode, body: error.toJSON() }; } if (error instanceof errors.ExtensionNotInstalledError) { - return error.toResponse(); + return { status: error.statusCode, body: error.toJSON() }; } - return Response.json({ - error: error instanceof Error ? error.message : "Unknown error", - }, { status: 500 }); + return { + status: 500, + body: { + error: error instanceof Error ? error.message : "Unknown error", + }, + }; } } - private async createIndex(request: Request): Promise { + async createIndex(body: unknown): Promise { try { - const data = await request.json(); - const body = CreateIndexDto.parse(data); - await this.remote.optimizer.createIndex(body.table, body.columns); - return Response.json({ success: true }); + const parsed = CreateIndexDto.parse(body); + await this.remote.optimizer.createIndex(parsed.table, parsed.columns); + return { status: 200, body: { success: true } }; } catch (error) { if (error instanceof ZodError) { - return Response.json({ - type: "error", - error: "invalid_body", - message: error.message, - }, { status: 400 }); + return { + status: 400, + body: { + type: "error", + error: "invalid_body", + message: error.message, + }, + }; } console.error("Failed to create index:", error); - return Response.json({ - error: error instanceof Error ? error.message : "Failed to create index", - }, { status: 500 }); + return { + status: 500, + body: { + error: + error instanceof Error ? error.message : "Failed to create index", + }, + }; } } - private onWebsocketRequest(request: Request): Response { - const { socket, response } = Deno.upgradeWebSocket(request); + onWebsocketConnection(socket: WebSocket): void { this.socket = socket; log.debug("Websocket connection established", "remote-controller"); - socket.addEventListener("close", () => { + socket.on("close", () => { log.debug("Websocket connection closed", "remote-controller"); - }, { once: true }); - - return response; + }); } private makeLoggingHandler(process: "pg_restore" | "pg_dump") { - return (log: string) => { - console.log(log); - this.socket?.send(JSON.stringify({ + return (logLine: string) => { + console.log(logLine); + this.sendToSocket({ type: "log", process, - log, - })); + log: logLine, + }); }; } private eventOnQueryProcessed(query: OptimizedQuery) { - this.socket?.send(JSON.stringify({ + this.sendToSocket({ type: "queryProcessed", query, - })); + }); } private eventError(error: Error, query: OptimizedQuery) { console.error(error); this.eventOnQueryProcessed(query); - this.socket?.send( - JSON.stringify({ - type: "error", - query, - error: error.message, - }), - ); + this.sendToSocket({ + type: "error", + query, + error: error.message, + }); } -} -function methodNotAllowed(): Response { - return Response.json("Method not allowed", { status: 405 }); + private sendToSocket(data: unknown) { + if (this.socket?.readyState === 1 /* OPEN */) { + this.socket.send(JSON.stringify(data)); + } + } } type DeltasResult = { diff --git a/src/remote/remote.test.ts b/src/remote/remote.test.ts index 96ea78a..2fdcadb 100644 --- a/src/remote/remote.test.ts +++ b/src/remote/remote.test.ts @@ -1,10 +1,11 @@ +import { test, expect, vi, afterEach } from "vitest"; import { PostgreSqlContainer } from "@testcontainers/postgresql"; import { Connectable } from "../sync/connectable.ts"; import { Remote } from "./remote.ts"; -import postgres from "postgresjs"; -import { assertEquals } from "@std/assert/equals"; +import { Pool } from "pg"; + import { ConnectionManager } from "../sync/connection-manager.ts"; -import { assertArrayIncludes } from "@std/assert"; + import { PgIdentifier } from "@query-doctor/core"; import { type Op } from "jsondiffpatch/formatters/jsonpatch"; @@ -34,15 +35,10 @@ export function testSpawnTarget( function assertOk( result: { type: string; value?: T }, ): asserts result is { type: "ok"; value: T } { - assertEquals(result.type, "ok"); + expect(result.type).toEqual("ok"); } -Deno.test({ - name: "syncs correctly", - sanitizeOps: false, - // deno is weird... the sync seems like it might be leaking resources? - sanitizeResources: false, - fn: async () => { +test("syncs correctly", async () => { const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ @@ -77,10 +73,10 @@ Deno.test({ const optimizedQueries = remote.optimizer.getQueries(); const queries = optimizedQueries.map((f) => f.query); - assertArrayIncludes(queries, [ + expect(queries).toEqual(expect.arrayContaining([ "create table testing(a int, b text);", "select * from testing where a = $1;", - ]); + ])); assertOk(result.schema); @@ -89,58 +85,41 @@ Deno.test({ ); console.log("tablenames", tableNames); - assertArrayIncludes(tableNames, ["testing"]); + expect(tableNames).toEqual(expect.arrayContaining(["testing"])); const indexNames = result.schema.value.indexes.map((index) => index.indexName.toString() ); - assertArrayIncludes(indexNames, ["testing_1234"]); + expect(indexNames).toEqual(expect.arrayContaining(["testing_1234"])); - const sql = postgres( - target.withDatabaseName(Remote.optimizingDbName).toString(), - ); + const pool = new Pool({ + connectionString: target.withDatabaseName(Remote.optimizingDbName).toString(), + }); const indexesAfter = - await sql`select indexname from pg_indexes where schemaname = 'public'`; - assertEquals( - indexesAfter.count, - 1, - "Indexes were not copied over correctly from the source db", - ); + await pool.query("select indexname from pg_indexes where schemaname = 'public'"); + expect(indexesAfter.rowCount).toEqual(1); - assertEquals(indexesAfter[0], { indexname: "testing_1234" }); + expect(indexesAfter.rows[0]).toEqual({ indexname: "testing_1234" }); const tablesAfter = - await sql`select tablename from pg_tables where schemaname = 'public'`; - assertEquals( - tablesAfter.count, - 1, - "Tables were not copied over correctly from the source db", - ); - assertEquals( - tablesAfter[0], - { tablename: "testing" }, - "Table name mismatch", - ); - const rows = await sql`select * from testing`; + await pool.query("select tablename from pg_tables where schemaname = 'public'"); + expect(tablesAfter.rowCount).toEqual(1); + expect(tablesAfter.rows[0]).toEqual({ tablename: "testing" }); + const rows = await pool.query("select * from testing"); // expect no rows to have been synced - assertEquals(rows.length, 0, "Table in target db not empty"); + expect(rows.rowCount, "Table in target db not empty").toEqual(0); - await sql.end(); + await pool.end(); } finally { await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); // Users who upgraded from Postgres 13/14 may have a leftover bit_xor aggregate. // It became built-in in Postgres 15, but custom versions from older installs remain. // This test ensures sync handles this gracefully. -Deno.test({ - name: "syncs database with custom bit_xor aggregate", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("syncs database with custom bit_xor aggregate", async () => { const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ @@ -181,23 +160,18 @@ Deno.test({ const tableNames = result.schema.value.tables.map((table) => table.tableName.toString() ); - assertArrayIncludes(tableNames, ["testing"]); + expect(tableNames).toEqual(expect.arrayContaining(["testing"])); const indexNames = result.schema.value.indexes.map((index) => index.indexName.toString() ); - assertArrayIncludes(indexNames, ["testing_idx"]); + expect(indexNames).toEqual(expect.arrayContaining(["testing_idx"])); } finally { await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); -Deno.test({ - name: "raw timescaledb syncs correctly", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("raw timescaledb syncs correctly", async () => { const [source, target] = await Promise.all([ new PostgreSqlContainer( "timescale/timescaledb:latest-pg17", @@ -235,24 +209,15 @@ Deno.test({ const indexesAfter = await t.exec( "select indexname from pg_indexes where schemaname = 'public'", ); - assertEquals( - indexesAfter.length, - 1, - "Indexes were not copied over correctly from the source db", - ); + expect(indexesAfter.length).toEqual(1); - assertEquals(indexesAfter[0], { indexname: "testing_1234" }); + expect(indexesAfter[0]).toEqual({ indexname: "testing_1234" }); } finally { await Promise.all([source.stop(), target.stop()]); } - }, }); -Deno.test({ - name: "infers '10k' stats strategy when row count is below threshold", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("infers '10k' stats strategy when row count is below threshold", async () => { // Create source with very few rows (below 5000 threshold) const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") @@ -284,22 +249,14 @@ Deno.test({ const result = await remote.syncFrom(source); await remote.optimizer.finish; - assertEquals( - result.meta.inferredStatsStrategy, - "10k", - "Should infer '10k' strategy for small databases", - ); + expect(result.meta.inferredStatsStrategy).toEqual("10k"); + await remote.cleanup(); } finally { await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); -Deno.test({ - name: "infers 'fromSource' stats strategy when row count is above threshold", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("infers 'fromSource' stats strategy when row count is above threshold", async () => { // Create source with many rows (above 5000 threshold) const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") @@ -331,22 +288,14 @@ Deno.test({ const result = await remote.syncFrom(source); await remote.optimizer.finish; - assertEquals( - result.meta.inferredStatsStrategy, - "fromSource", - "Should infer 'fromSource' strategy for large databases", - ); + expect(result.meta.inferredStatsStrategy).toEqual("fromSource"); + await remote.cleanup(); } finally { await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); -Deno.test({ - name: "timescaledb with continuous aggregates sync correctly", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("timescaledb with continuous aggregates sync correctly", async () => { const [source, target] = await Promise.all([ new PostgreSqlContainer( "timescale/timescaledb:latest-pg17", @@ -406,30 +355,21 @@ Deno.test({ const queries = remote.optimizer.getQueries(); const queryStrings = queries.map((q) => q.query); - assertArrayIncludes(queryStrings, [ + expect(queryStrings).toEqual(expect.arrayContaining([ "select * from conditions where time < now();", - ]); + ])); const indexesAfter = await t.exec( "select indexname from pg_indexes where schemaname = 'public';", ); - assertEquals( - indexesAfter.length, - 1, - "Indexes were not copied over correctly from the source db", - ); + expect(indexesAfter.length).toEqual(1); - assertEquals(indexesAfter[0], { indexname: "conditions_time_idx" }); + expect(indexesAfter[0]).toEqual({ indexname: "conditions_time_idx" }); } finally { await Promise.all([source.stop(), target.stop()]); } - }, }); -Deno.test({ - name: "schema loader detects changes after database modification", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("schema loader detects changes after database modification", async () => { const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ @@ -456,28 +396,20 @@ Deno.test({ const manager = ConnectionManager.forLocalDatabase(); await using remote = new Remote(target, manager); - const sourcePg = postgres(source.toString()); + const sourcePg = new Pool({ connectionString: source.toString() }); await remote.syncFrom(source); await remote.optimizer.finish; const initialStatus = await remote.getStatus(); const initialDiffsResult = initialStatus.diffs; - assertEquals( - initialDiffsResult.status, - "fulfilled", - "Schema poll should succeed", - ); + expect(initialDiffsResult.status, "Schema poll should succeed").toEqual("fulfilled"); const initialDiffs = initialDiffsResult.status === "fulfilled" ? initialDiffsResult.value : []; - assertEquals( - initialDiffs.length, - 0, - "Should have no diffs initially after sync", - ); + expect(initialDiffs.length, "Should have no diffs initially after sync").toEqual(0); - await sourcePg.unsafe(` + await sourcePg.query(` alter table testing add column c int; create index "testing_c_idx" on testing(c); `); @@ -485,41 +417,28 @@ Deno.test({ const statusAfterChange = await remote.getStatus(); const diffsResult = statusAfterChange.diffs; - assertEquals( - diffsResult.status, - "fulfilled", - "Schema poll should succeed", - ); + expect(diffsResult.status, "Schema poll should succeed").toEqual("fulfilled"); const diffs = diffsResult.status === "fulfilled" ? diffsResult.value : []; - assertEquals( - diffs.length, - 2, - "Should detect 2 schema changes (added column and index)", - ); + expect(diffs.length, "Should detect 2 schema changes (added column and index)").toEqual(2); const addedColumnDiff = diffs.find((diff: Op) => typeof diff.path === "string" && diff.path.includes("columns") ); - assertEquals(addedColumnDiff?.op, "add", "Should detect column addition"); + expect(addedColumnDiff?.op, "Should detect column addition").toEqual("add"); const addedIndexDiff = diffs.find((diff: Op) => typeof diff.path === "string" && diff.path.includes("indexes") ); - assertEquals(addedIndexDiff?.op, "add", "Should detect index addition"); + expect(addedIndexDiff?.op, "Should detect index addition").toEqual("add"); await sourcePg.end(); } finally { await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); -Deno.test({ - name: "returns extension error when pg_stat_statements is not installed", - sanitizeOps: false, - sanitizeResources: false, - fn: async () => { +test("returns extension error when pg_stat_statements is not installed", async () => { const [sourceDb, targetDb] = await Promise.all([ new PostgreSqlContainer("postgres:17") .withCopyContentToContainer([ @@ -540,28 +459,31 @@ Deno.test({ const target = Connectable.fromString(targetDb.getConnectionUri()); const source = Connectable.fromString(sourceDb.getConnectionUri()); - await using remote = new Remote( + const remote = new Remote( target, ConnectionManager.forLocalDatabase(), ); - const result = await remote.syncFrom(source); - - // Schema should still sync successfully - assertOk(result.schema); - - const tableNames = result.schema.value.tables.map((table) => - table.tableName.toString() - ); - assertArrayIncludes(tableNames, ["testing"]); - - // Should return the extension error for recent queries - assertEquals(result.recentQueriesError, { - type: "extension_not_installed", - extensionName: "pg_stat_statements", - }); + try { + const result = await remote.syncFrom(source); + + // Schema should still sync successfully + assertOk(result.schema); + + const tableNames = result.schema.value.tables.map((table) => + table.tableName.toString() + ); + expect(tableNames).toContain("testing"); + + // Should return the extension error for recent queries + expect(result.recentQueriesError).toEqual({ + type: "extension_not_installed", + extensionName: "pg_stat_statements", + }); + } finally { + await remote.cleanup(); + } } finally { await Promise.all([sourceDb.stop(), targetDb.stop()]); } - }, }); diff --git a/src/reporters/github/github.ts b/src/reporters/github/github.ts index ac3e4a8..f79b053 100644 --- a/src/reporters/github/github.ts +++ b/src/reporters/github/github.ts @@ -1,7 +1,13 @@ import * as github from "@actions/github"; import * as core from "@actions/core"; -import success from "./success.md.j2" with { type: "text" }; -import * as n from "nunjucks"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const success = readFileSync(join(__dirname, "success.md.j2"), "utf-8"); +import n from "nunjucks"; import { isQueryLong, renderExplain, diff --git a/src/runner.ts b/src/runner.ts index a35ff70..8c2bcb6 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -3,6 +3,8 @@ import * as prettier from "prettier"; import prettierPluginSql from "prettier-plugin-sql"; import csv from "fast-csv"; import { Readable } from "node:stream"; +import { statSync, readFileSync } from "node:fs"; +import { spawn } from "node:child_process"; import { fingerprint } from "@libpg-query/parser"; import { preprocessEncodedJson } from "./sql/json.ts"; import { @@ -24,7 +26,9 @@ import { type ReportQueryCostWarning, type ReportStatistics, } from "./reporters/reporter.ts"; -import { bgBrightMagenta, blue, yellow } from "@std/fmt/colors"; +const bgBrightMagenta = (s: string) => `\x1b[105m${s}\x1b[0m`; +const yellow = (s: string) => `\x1b[33m${s}\x1b[0m`; +const blue = (s: string) => `\x1b[34m${s}\x1b[0m`; import { env } from "./env.ts"; import { connectToSource } from "./sql/postgresjs.ts"; import { parse } from "@libpg-query/parser"; @@ -68,7 +72,7 @@ export class Runner { async run() { const startDate = new Date(); - const logSize = Deno.statSync(this.logPath).size; + const logSize = statSync(this.logPath).size; console.log(`logPath=${this.logPath},fileSize=${logSize}`); const args = [ "--dump-raw-csv", @@ -77,17 +81,12 @@ export class Runner { "stderr", this.logPath, ]; - const command = new Deno.Command("pgbadger", { - stdout: "piped", - stderr: "piped", - args, - }); console.log(`pgbadger ${args.join(" ")}`); - const output = command.spawn(); - output.stderr.pipeTo(Deno.stderr.writable); + const child = spawn("pgbadger", args, { stdio: ["ignore", "pipe", "pipe"] }); + child.stderr!.pipe(process.stderr); let error: Error | undefined; const stream = csv - .parseStream(Readable.from(output.stdout), { + .parseStream(child.stdout!, { headers: false, }) .on("error", (err) => { @@ -149,7 +148,7 @@ export class Runner { break; } } - await output.status; + await new Promise((resolve) => child.on("close", () => resolve())); console.log( `Matched ${this.queryStats.matched} queries out of ${this.queryStats.total}`, ); @@ -362,7 +361,7 @@ export class Runner { } } private static readStatisticsFile(path: string): ExportedStats[] { - const data = Deno.readFileSync(path); + const data = readFileSync(path); const json = JSON.parse(new TextDecoder().decode(data)); return ExportedStats.array().parse(json); } diff --git a/src/server/http.ts b/src/server/http.ts index b3b2d89..1e665e4 100644 --- a/src/server/http.ts +++ b/src/server/http.ts @@ -1,13 +1,14 @@ import { SpanStatusCode, trace } from "@opentelemetry/api"; +import Fastify, { type FastifyInstance } from "fastify"; +import cors from "@fastify/cors"; +import rateLimit from "@fastify/rate-limit"; +import websocket from "@fastify/websocket"; import { PostgresSyncer } from "../sync/syncer.ts"; import { log } from "../log.ts"; -import * as limiter from "./rate-limit.ts"; import { LiveQueryRequest, SyncRequest } from "./sync.dto.ts"; import { ZodError } from "zod"; -import { shutdownController } from "../shutdown.ts"; import { env } from "../env.ts"; import { SyncResult } from "../sync/syncer.ts"; -import type { RateLimitResult } from "@rabbit-company/rate-limiter"; import * as errors from "../sync/errors.ts"; import { RemoteController } from "../remote/remote-controller.ts"; import { Connectable } from "../sync/connectable.ts"; @@ -18,38 +19,33 @@ const sourceConnectionManager = ConnectionManager.forRemoteDatabase(); const syncer = new PostgresSyncer(sourceConnectionManager); -async function onSync(req: Request) { +async function onSync(body: unknown) { const startTime = Date.now(); - const url = new URL(req.url); - if (!req.body) { - return new Response("Missing body", { status: 400 }); - } - let body: SyncRequest; - const bodyString = await req.text(); + let parsed: SyncRequest; try { - body = SyncRequest.parse(JSON.parse(bodyString)); + parsed = SyncRequest.parse(body); } catch (e: unknown) { if (e instanceof ZodError) { - return Response.json( - { + return { + status: 400, + body: { kind: "error", type: "invalid_body", error: e.issues.map((issue) => issue.message).join("\n"), }, - { status: 400 }, - ); + }; } - return Response.json( - { + return { + status: 400, + body: { kind: "error", type: "unexpected_error", error: String(e), }, - { status: 400 }, - ); + }; } - const { seed, requiredRows, maxRows } = body; + const { seed, requiredRows, maxRows } = parsed; const span = trace.getActiveSpan(); if (requiredRows > maxRows) { log.warn( @@ -64,192 +60,180 @@ async function onSync(req: Request) { ); } span?.setAttribute("requiredRows", requiredRows); - span?.setAttribute("db.host", url.hostname); let result: SyncResult; try { - result = await syncer.syncDDL(body.db, { + result = await syncer.syncDDL(parsed.db, { requiredRows, maxRows, seed, }); } catch (error) { if (error instanceof errors.ExtensionNotInstalledError) { - return error.toResponse(); + return { status: error.statusCode ?? 500, body: error.toJSON() }; } else if (error instanceof errors.MaxTableIterationsReached) { - return error.toResponse(); + return { status: error.statusCode ?? 500, body: error.toJSON() }; } - return makeUnexpectedErrorResponse(error); + return makeUnexpectedErrorResult(error); } span?.setStatus({ code: SpanStatusCode.OK }); log.info(`Sent sync response in ${Date.now() - startTime}ms`, "http:sync"); - return Response.json( - { - kind: "ok", - ...result, - }, - { status: 200 }, - ); + return { + status: 200, + body: { kind: "ok", ...result }, + }; } -async function onSyncLiveQuery(req: Request) { - let body: LiveQueryRequest; +async function onSyncLiveQuery(body: unknown) { + let parsed: LiveQueryRequest; try { - body = LiveQueryRequest.parse(await req.json()); + parsed = LiveQueryRequest.parse(body); } catch (e: unknown) { if (e instanceof ZodError) { - return Response.json( - { + return { + status: 400, + body: { kind: "error", type: "invalid_body", error: e.issues.map((issue) => issue.message).join("\n"), }, - { status: 400 }, - ); + }; } throw e; } try { - const { queries, deltas } = await syncer.liveQuery(body.db); - return Response.json({ kind: "ok", queries, deltas }, { status: 200 }); + const { queries, deltas } = await syncer.liveQuery(parsed.db); + return { status: 200, body: { kind: "ok", queries, deltas } }; } catch (error) { if (error instanceof errors.ExtensionNotInstalledError) { - return error.toResponse(); + return { status: error.statusCode ?? 500, body: error.toJSON() }; } else if (error instanceof errors.PostgresError) { - return error.toResponse(); + return { status: error.statusCode ?? 500, body: error.toJSON() }; } - return makeUnexpectedErrorResponse(error); + return makeUnexpectedErrorResult(error); } } -export function createServer( +export async function createServer( hostname: string, port: number, targetDb?: Connectable, -) { +): Promise { + const fastify = Fastify({ logger: false }); + + await fastify.register(cors, { + origin: "*", + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type"], + exposedHeaders: [ + "Content-Type", + "X-RateLimit-Limit", + "X-RateLimit-Remaining", + "X-RateLimit-Reset", + ], + maxAge: 86400, + }); + + if (env.HOSTED) { + await fastify.register(rateLimit, { + max: 100, + timeWindow: "15 minutes", + }); + } + + await fastify.register(websocket); + const optimizingDbConnectionManager = ConnectionManager.forLocalDatabase(); const remoteController = targetDb ? new RemoteController( - new Remote(targetDb, optimizingDbConnectionManager), - ) + new Remote(targetDb, optimizingDbConnectionManager), + ) : undefined; - return Deno.serve( - { hostname, port, signal: shutdownController.signal }, - async (req, info) => { - const url = new URL(req.url); - log.http(req); - - if (req.method === "OPTIONS") { - return transformResponse( - new Response("OK", { - status: 200, - headers: corsHeaders, - }), - ); - } - if (url.pathname === "/") { - return Response.redirect( - "https://github.com/Query-Doctor/analyzer", - 307, - ); - } - if (url.pathname === "/health") { - return transformResponse( - new Response(JSON.stringify({ status: "ok" }), { - status: 200, - headers: { - "Content-Type": "application/json", - ...corsHeaders, - }, - }), - ); - } - let limit: RateLimitResult | undefined; - if (env.HOSTED) { - limit = limiter.sync.check(url.pathname, info.remoteAddr.hostname); - if (limit.limited) { - return limiter.appendHeaders( - new Response("Rate limit exceeded", { status: 429 }), - limit, - ); - } - } - try { - if (url.pathname === "/postgres/all") { - if (req.method !== "POST") { - return new Response("Method not allowed", { status: 405 }); - } - const res = await onSync(req); - return transformResponse(res, limit); - } else if (url.pathname === "/postgres/live") { - if (req.method !== "POST") { - return new Response("Method not allowed", { status: 405 }); - } - const res = await onSyncLiveQuery(req); - return transformResponse(res, limit); - } - const remoteResponse = await remoteController?.execute(req); - if (remoteResponse) { - // WebSocket upgrade responses have immutable headers, skip transform - if (req.headers.get("upgrade") === "websocket") { - return remoteResponse; - } - return transformResponse(remoteResponse, limit); - } - return new Response("Not found", { status: 404 }); - } catch (error) { - return transformResponse( - new Response( - JSON.stringify({ - error: error instanceof Error - ? error.message - : "Internal server error", - }), - { - status: 500, - }, - ), - limit, - ); - } - }, - ); -} -function transformResponse(res: Response, limit?: RateLimitResult): Response { - for (const [key, value] of Object.entries(corsHeaders)) { - res.headers.set(key, value); - } - if (limit) { - limiter.appendHeaders(res, limit); + fastify.get("/", async (_request, reply) => { + return reply.redirect("https://github.com/Query-Doctor/analyzer", 307); + }); + + fastify.get("/health", async (_request, reply) => { + return reply.send({ status: "ok" }); + }); + + fastify.post("/postgres/all", async (request, reply) => { + log.info(`[POST] /postgres/all`, "http"); + const result = await onSync(request.body); + return reply.status(result.status).send(result.body); + }); + + fastify.post("/postgres/live", async (request, reply) => { + log.info(`[POST] /postgres/live`, "http"); + const result = await onSyncLiveQuery(request.body); + return reply.status(result.status).send(result.body); + }); + + if (remoteController) { + fastify.post("/postgres", async (request, reply) => { + log.info(`[POST] /postgres`, "http"); + const result = await remoteController.onFullSync( + JSON.stringify(request.body), + ); + return reply.status(result.status).send(result.body); + }); + + fastify.get("/postgres", async (request, reply) => { + log.info(`[GET] /postgres`, "http"); + const result = await remoteController.getStatus(); + return reply.send(result); + }); + + fastify.register(async function (app) { + app.get( + "/postgres/ws", + { websocket: true }, + (socket, _request) => { + remoteController.onWebsocketConnection(socket); + }, + ); + }); + + fastify.post("/postgres/indexes", async (request, reply) => { + log.info(`[POST] /postgres/indexes`, "http"); + const result = await remoteController.createIndex(request.body); + return reply.status(result.status).send(result.body); + }); + + fastify.post("/postgres/indexes/toggle", async (request, reply) => { + log.info(`[POST] /postgres/indexes/toggle`, "http"); + const result = await remoteController.toggleIndex(request.body); + return reply.status(result.status).send(result.body); + }); + + fastify.post("/postgres/reset", async (request, reply) => { + log.info(`[POST] /postgres/reset`, "http"); + const result = await remoteController.onReset( + JSON.stringify(request.body), + ); + return reply.status(result.status).send(result.body); + }); } - return res; + + await fastify.listen({ host: hostname, port }); + return fastify; } -const corsHeaders = { - "Access-Control-Allow-Origin": "*", - // cache the preflight requests for 1 day - "Access-Control-Max-Age": "86400", - "Access-Control-Allow-Methods": "POST", - "Access-Control-Allow-Headers": "Content-Type", - "Access-Control-Expose-Headers": - "Content-Type, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset", -}; - -export function makeUnexpectedErrorResponse(error: unknown): Response { +function makeUnexpectedErrorResult(error: unknown) { if (error instanceof Error && !env.HOSTED) { - return Response.json( - { kind: "error", type: "unexpected_error", error: error.message }, - { status: 500 }, - ); + return { + status: 500, + body: { kind: "error", type: "unexpected_error", error: error.message }, + }; } console.error(error); - return Response.json( - { + return { + status: 500, + body: { kind: "error", type: "unexpected_error", error: "Internal Server Error", }, - { status: 500 }, - ); + }; } diff --git a/src/server/rate-limit.ts b/src/server/rate-limit.ts deleted file mode 100644 index eb24c06..0000000 --- a/src/server/rate-limit.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { RateLimiter, RateLimitResult } from "@rabbit-company/rate-limiter"; - -export const sync = new RateLimiter({ - window: 15 * 60 * 1000, // 15 minutes (default: 1 minute) - max: 100, // Limit each identifier to 100 requests per window (default: 60) - cleanupInterval: 60 * 1000, // Cleanup every minute (default: 30 seconds) - enableCleanup: true, // Enable automatic cleanup (default: true) -}); - -export function appendHeaders( - res: Response, - result: RateLimitResult, -): Response { - res.headers.set("X-RateLimit-Limit", result.limit.toString()); - res.headers.set("X-RateLimit-Window", "15m"); - res.headers.set("X-RateLimit-Remaining", result.remaining.toString()); - const ratelimitReset = new Date(result.reset).toUTCString(); - if (result.limited) { - res.headers.set("Retry-After", ratelimitReset); - } else { - res.headers.set("X-RateLimit-Reset", ratelimitReset); - } - return res; -} diff --git a/src/shutdown.ts b/src/shutdown.ts index bc2428f..6343f5c 100644 --- a/src/shutdown.ts +++ b/src/shutdown.ts @@ -1,6 +1 @@ export const shutdownController = new AbortController(); - -export function shutdown() { - shutdownController.abort(); - Deno.exit(0); -} diff --git a/src/sql/json.ts b/src/sql/json.ts index 5c539b2..226c83a 100644 --- a/src/sql/json.ts +++ b/src/sql/json.ts @@ -27,7 +27,7 @@ function unescapeEncodedJson(jsonString: string) { .replace(/\\n/g, "\n") // there are random control characters in the json lol .replace( - // deno-lint-ignore no-control-regex + /[\u0000-\u001F]+/g, (c) => c === "\n" ? "\\n" : c === "\r" ? "\\r" : c === "\t" ? "\\t" : "", diff --git a/src/sql/postgresjs.ts b/src/sql/postgresjs.ts index 3b8b047..94714eb 100644 --- a/src/sql/postgresjs.ts +++ b/src/sql/postgresjs.ts @@ -1,4 +1,5 @@ -import postgres from "postgresjs"; +import { Pool, type PoolConfig, type PoolClient } from "pg"; +import Cursor from "pg-cursor"; import { type Postgres, type PostgresTransaction, @@ -7,42 +8,38 @@ import { import { Connectable } from "../sync/connectable.ts"; import { log } from "../log.ts"; -type PgConnectionOptions = postgres.Options< - Record ->; - const DEFAULT_ITEMS_PER_PAGE = 20; // we want to set a very low idle timeout to prevent // clogging up connections -const DEFAULT_IDLE_TIMEOUT_SECONDS = 15; +const DEFAULT_IDLE_TIMEOUT_MS = 15_000; // it's ok to recycle connections frequently if needed -const DEFAULT_MAX_LIFETIME_SECONDS = 60 * 5; +const DEFAULT_MAX_LIFETIME_MS = 60 * 5 * 1000; /** * Connecting to the local optimizer */ export function connectToOptimizer(connectable: Connectable) { const hostname = connectable.url.searchParams.get("host"); - const baseConnectionOptions: PgConnectionOptions = { + const baseConfig: PoolConfig = { max: 100, }; if (hostname) { const database = connectable.url.pathname.slice(1); - const connectionOptions: PgConnectionOptions = { - ...baseConnectionOptions, - username: "postgres", + const config: PoolConfig = { + ...baseConfig, + user: "postgres", database, - hostname, + host: hostname, }; - const pg = postgres(connectionOptions); - return wrapGenericPostgresInterface(pg); + const pool = new Pool(config); + return wrapPgPool(pool); } else { log.info( `Connecting to optimizing db ${connectable} using custom POSTGRES_URL`, "postgres", ); - return connect(connectable, baseConnectionOptions); + return connect(connectable, baseConfig); } } @@ -55,57 +52,126 @@ export function connectToOptimizer(connectable: Connectable) { export function connectToSource( connectable: Connectable, ) { - const connectionOptions: PgConnectionOptions = { + const config: PoolConfig = { max: 20, - max_lifetime: DEFAULT_MAX_LIFETIME_SECONDS, - idle_timeout: DEFAULT_IDLE_TIMEOUT_SECONDS, + idleTimeoutMillis: DEFAULT_IDLE_TIMEOUT_MS, }; - return connect(connectable, connectionOptions); + return connect(connectable, config); +} + +function connect(connectable: Connectable, config: PoolConfig) { + const pool = new Pool({ + ...config, + connectionString: connectable.toString(), + }); + return wrapPgPool(pool); +} + +/** + * Pre-serialize array/object params as JSON strings. + * pg serializes arrays as PostgreSQL array literals ({...}), + * but the Postgres interface expects postgres.js semantics + * where complex types are sent as JSON strings ([...]). + */ +function serializeParams(params?: unknown[]): unknown[] | undefined { + if (!params) return params; + return params.map((p) => { + if (p === null || p === undefined) return p; + if (Array.isArray(p)) return JSON.stringify(p); + if (typeof p === "object" && !(p instanceof Buffer)) return JSON.stringify(p); + return p; + }); } -function connect(connectable: Connectable, options: PgConnectionOptions) { - const pg = postgres(connectable.toString(), options); - return wrapGenericPostgresInterface(pg); +/** + * Format a string array as a PostgreSQL array literal. + * Use this for params that target ::text[] casts instead of ::jsonb, + * so they bypass JSON serialization in serializeParams. + */ +export function toPgTextArray(arr: string[]): string { + if (arr.length === 0) return "{}"; + return "{" + arr.map((s) => '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"').join(",") + "}"; } -export function wrapGenericPostgresInterface(pg: postgres.Sql): Postgres { +export function wrapPgPool(pool: Pool): Postgres { + // Handle idle client errors to prevent process crashes. + // Expected during DROP DATABASE ... WITH (FORCE) which terminates + // all connections to the target database. + pool.on("error", (err) => { + log.warn(`Pool idle client error: ${err.message}`, "postgres"); + }); + return { - exec: (query, params) => { - return pg.unsafe(query, params as postgres.ParameterOrJSON[]); + exec: async (query, params) => { + const result = await pool.query(query, serializeParams(params) as any[]); + return result.rows; + }, + serverNum: async () => { + const result = await pool.query("show server_version_num"); + return PostgresVersion.parse(result.rows[0].server_version_num); }, - serverNum: async () => - PostgresVersion.parse( - (await pg.unsafe(`show server_version_num`))[0].server_version_num, - ), transaction: async ( callback: (tx: PostgresTransaction) => Promise, ) => { - const result = await pg.begin((tx) => { + const client = await pool.connect(); + try { + await client.query("BEGIN"); + let savepointCounter = 0; + // Serialize exec calls to prevent savepoint interleaving + // when callers use Promise.all (matches postgres.js behavior) + let queue: Promise = Promise.resolve(); const transaction: PostgresTransaction = { - exec: tx.unsafe, + exec: (query, params) => { + const doExec = async () => { + const sp = "sp_" + savepointCounter++; + await client.query("SAVEPOINT " + sp); + try { + const result = await client.query(query, serializeParams(params) as any[]); + await client.query("RELEASE SAVEPOINT " + sp); + return result.rows; + } catch (error) { + await client.query("ROLLBACK TO SAVEPOINT " + sp); + throw error; + } + }; + const result = queue.then(doExec, doExec); + queue = result.then(() => {}, () => {}); + return result; + }, }; - return callback(transaction); - }); - // TODO: is this safe? - return result as Promise; + const result = await callback(transaction); + await client.query("COMMIT"); + return result; + } catch (error) { + await client.query("ROLLBACK"); + throw error; + } finally { + client.release(); + } }, async *cursor( query: string, params?: unknown[], options?: { size?: number }, ) { - const result = pg - .unsafe(query, params as postgres.ParameterOrJSON[]) - .cursor(options?.size ?? DEFAULT_ITEMS_PER_PAGE); - for await (const row of result) { - // TODO: is this safe? - yield* row as T[]; + const client = await pool.connect(); + try { + const cursor = client.query(new Cursor(query, serializeParams(params) as any[])); + const batchSize = options?.size ?? DEFAULT_ITEMS_PER_PAGE; + let rows = await cursor.read(batchSize); + while (rows.length > 0) { + yield* rows as T[]; + rows = await cursor.read(batchSize); + } + await cursor.close(); + } finally { + client.release(); } }, // @ts-expect-error | this will be added to the pg interface later close() { - return pg.end(); + return pool.end(); }, }; } diff --git a/src/sql/recent-query.ts b/src/sql/recent-query.ts index b1e472f..69ffeed 100644 --- a/src/sql/recent-query.ts +++ b/src/sql/recent-query.ts @@ -1,6 +1,5 @@ import * as prettier from "prettier"; import prettierPluginSql from "prettier-plugin-sql"; -// deno-lint-ignore no-unused-vars import type { SegmentedQueryCache } from "../sync/seen-cache.ts"; import { Analyzer, diff --git a/src/sync/connectable.test.ts b/src/sync/connectable.test.ts index ce69571..95f5bbc 100644 --- a/src/sync/connectable.test.ts +++ b/src/sync/connectable.test.ts @@ -1,16 +1,15 @@ -import { assertEquals } from "@std/assert"; +import { test, expect } from "vitest"; import { Connectable } from "./connectable.ts"; import { PgIdentifier } from "@query-doctor/core"; -Deno.test("connectable", () => { +test("connectable", () => { const connectable = Connectable.fromString( "postgres://user:password@localhost:5432/dbname?a=b&c=d", ); const newConnectable = connectable.withDatabaseName( PgIdentifier.fromString("testing"), ); - assertEquals( - newConnectable.toString(), + expect(newConnectable.toString()).toEqual( "postgres://user:password@localhost:5432/testing?a=b&c=d", ); }); diff --git a/src/sync/errors.ts b/src/sync/errors.ts index 615a374..88fc51d 100644 --- a/src/sync/errors.ts +++ b/src/sync/errors.ts @@ -1,50 +1,59 @@ export class PostgresError extends Error { + readonly statusCode = 500; + constructor(message: string) { super(message); } + toJSON() { + return { + kind: "error" as const, + type: "unexpected_error" as const, + error: this.message, + }; + } + toResponse(): Response { - return Response.json( - { - kind: "error", - type: "unexpected_error", - error: this.message, - }, - { status: 500 }, - ); + return Response.json(this.toJSON(), { status: this.statusCode }); } } export class ExtensionNotInstalledError extends Error { + readonly statusCode = 400; + constructor(public readonly extension: string) { super(`extension ${extension} is not installed`); } + toJSON() { + return { + kind: "error" as const, + type: "extension_not_installed" as const, + extensionName: this.message, + }; + } + toResponse(): Response { - return Response.json( - { - kind: "error", - type: "extension_not_installed", - extensionName: this.message, - }, - { status: 400 }, - ); + return Response.json(this.toJSON(), { status: this.statusCode }); } } export class MaxTableIterationsReached extends Error { + readonly statusCode = 500; + constructor(public readonly maxIterations: number) { super(`max table iterations reached: ${maxIterations}`); } + toJSON() { + return { + kind: "error" as const, + type: "max_table_iterations_reached" as const, + error: "Max table iterations reached. This is a bug with the syncer", + }; + } + toResponse(): Response { - return Response.json( - { - kind: "error", - type: "max_table_iterations_reached", - error: "Max table iterations reached. This is a bug with the syncer", - }, - { status: 500 }, - ); + return Response.json(this.toJSON(), { status: this.statusCode }); } } diff --git a/src/sync/executable.ts b/src/sync/executable.ts index 91ed8c9..f030525 100644 --- a/src/sync/executable.ts +++ b/src/sync/executable.ts @@ -1,17 +1,16 @@ +import { spawnSync } from "node:child_process"; +import { statSync } from "node:fs"; import { env } from "../env.ts"; import { log } from "../log.ts"; -const decoder = new TextDecoder(); - -// Is there a way to get this working for windows? function lookupBinary( - os: typeof Deno.build.os, + platform: typeof process.platform, name: string, ): string | undefined { try { - if (os === "linux" || os === "darwin") { - const output = new Deno.Command("which", { args: [name] }).outputSync(); - return decoder.decode(output.stdout) || undefined; + if (platform === "linux" || platform === "darwin") { + const output = spawnSync("which", [name]); + return output.stdout.toString() || undefined; } } catch (_error) { // it's not in path. No problem @@ -28,9 +27,10 @@ export function findPgRestoreBinary(version: string): string { printVersion("pg_restore", forcePath); return forcePath; } - const os = Deno.build.os; - const arch = Deno.build.arch; - const existing = lookupBinary(os, "pg_restore")?.trim(); + const platform = process.platform; + const arch = process.arch; + const os = platformToOs(platform); + const existing = lookupBinary(platform, "pg_restore")?.trim(); if (existing) { log.info( `Using pg_restore binary from PATH: ${existing.trim()}`, @@ -40,7 +40,7 @@ export function findPgRestoreBinary(version: string): string { return existing; } const shippedPath = `./bin/pg_restore-${version}/pg_restore.${os}-${arch}`; - if (!Deno.statSync(shippedPath).isFile) { + if (!statSync(shippedPath).isFile()) { throw new Error(`pg_restore binary not found at ${shippedPath}`); } log.info( @@ -61,9 +61,10 @@ export function findPgDumpBinary(version: string): string { printVersion("pg_dump", forcePath); return forcePath; } - const os = Deno.build.os; - const arch = Deno.build.arch; - const existing = lookupBinary(os, "pg_dump")?.trim(); + const platform = process.platform; + const arch = process.arch; + const os = platformToOs(platform); + const existing = lookupBinary(platform, "pg_dump")?.trim(); if (existing) { log.info( `Using pg_dump binary from PATH: ${existing}`, @@ -73,7 +74,7 @@ export function findPgDumpBinary(version: string): string { return existing; } const shippedPath = `./bin/pg_dump-${version}/pg_dump.${os}-${arch}`; - if (!Deno.statSync(shippedPath).isFile) { + if (!statSync(shippedPath).isFile()) { throw new Error(`pg_dump binary not found at ${shippedPath}`); } log.info(`Using built-in "pg_dump" binary: ${shippedPath}`, "schema:setup"); @@ -87,11 +88,14 @@ function printVersion(name: string, executable: string) { } function getVersion(executable: string) { - const version = new Deno.Command(executable, { - args: ["--version"], - stdout: "piped", - // we want to be able to see the errors directly - stderr: "inherit", + const result = spawnSync(executable, ["--version"], { + stdio: ["pipe", "pipe", "inherit"], }); - return new TextDecoder().decode((version.outputSync()).stdout).trim(); + return result.stdout.toString().trim(); +} + +/** Map Node.js process.platform to the OS names used in binary paths */ +function platformToOs(platform: typeof process.platform): string { + if (platform === "win32") return "windows"; + return platform; } diff --git a/src/sync/pg-connector.ts b/src/sync/pg-connector.ts index a22d704..a412984 100644 --- a/src/sync/pg-connector.ts +++ b/src/sync/pg-connector.ts @@ -1,4 +1,6 @@ -import schemaDumpSql from "./schema_dump.sql" with { type: "text" }; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; import type { CursorOptions, DatabaseConnector, @@ -21,9 +23,13 @@ import { SegmentedQueryCache } from "./seen-cache.ts"; import { FullSchema, FullSchemaColumn } from "./schema_differ.ts"; import { ExtensionNotInstalledError, PostgresError } from "./errors.ts"; import { RawRecentQuery, RecentQuery } from "../sql/recent-query.ts"; -// deno-lint-ignore no-unused-vars +import { toPgTextArray } from "../sql/postgresjs.ts"; import { ConnectionManager } from "./connection-manager.ts"; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const schemaDumpSql = readFileSync(join(__dirname, "schema_dump.sql"), "utf-8"); + const ctidSymbol = Symbol("ctid"); type Row = NonNullable & { [ctidSymbol]: string; @@ -179,7 +185,7 @@ ORDER BY pg_tables.tablename, fk."referencedTable", fk."sourceColumn";-- @qd_introspection `, [ - options.excludedSchemas, + toPgTextArray(options.excludedSchemas), ], ), )(); @@ -439,7 +445,7 @@ ORDER BY JOIN unnest($1::text[], $2::text[]) AS t(schema_name, table_name) ON n.nspname = t.schema_name AND c.relname = t.table_name WHERE c.relkind IN ('r', 'm')`, - [schemaNames, tableNames], + [toPgTextArray(schemaNames), toPgTextArray(tableNames)], ); return Number(results[0]?.total_rows ?? 0); } @@ -493,6 +499,9 @@ ORDER BY results, ); } catch (err) { + if (err instanceof ExtensionNotInstalledError) { + throw err; + } if ( err instanceof Error && err.message.includes('relation "pg_stat_statements" does not exist') diff --git a/src/sync/schema-link.ts b/src/sync/schema-link.ts index 26caa33..d5863ac 100644 --- a/src/sync/schema-link.ts +++ b/src/sync/schema-link.ts @@ -1,4 +1,5 @@ import { SpanStatusCode, trace } from "@opentelemetry/api"; +import { spawn, type ChildProcess } from "node:child_process"; import { log } from "../log.ts"; import { shutdownController } from "../shutdown.ts"; import { withSpan } from "../otel.ts"; @@ -12,6 +13,11 @@ export type TableStats = { export type DumpTargetType = "pglite" | "native-postgres"; +export type CommandStatus = { + code: number | null; + success: boolean; +}; + export class PostgresSchemaLink { constructor( public readonly connectable: Connectable, @@ -84,7 +90,7 @@ export class DumpCommand // we don't want to allow callers to construct an instance // with any arbitrary child process. Use the static method instead - private constructor(private readonly process: Deno.ChildProcess) { + private constructor(private readonly childProcess: ChildProcess) { super(); } @@ -124,96 +130,86 @@ export class DumpCommand ...DumpCommand.extraFlags(connectable), connectable.toString(), ]; - const command = new Deno.Command(DumpCommand.binaryPath, { - args, - stdin: "null", - stdout: "piped", - stderr: "piped", + + const childProcess = spawn(DumpCommand.binaryPath, args, { + stdio: ["ignore", "pipe", "pipe"], signal: shutdownController.signal, }); - const process = command.spawn(); - - return new DumpCommand(process); + return new DumpCommand(childProcess); } - async collectOutput(): Promise { + collectOutput(): Promise { const span = trace.getActiveSpan(); - const decoder = new TextDecoder(); - const output = await this.process.output(); - span?.setAttribute("outputBytes", output.stdout.byteLength); - return withSpan("decodeResponse", () => { - const stderr = output.stderr.byteLength > 0 - ? decoder.decode(output.stderr) - : undefined; - if (stderr) { - console.warn(stderr); - } - if (output.code !== 0) { - span?.setStatus({ code: SpanStatusCode.ERROR, message: stderr }); - log.error(`Error: ${stderr}`, "schema:sync"); - throw new Error(stderr); - } - log.info( - `Dumped schema. bytes=${output.stdout.byteLength}`, - "schema:sync", - ); - const stdout = decoder.decode(output.stdout); - return { stdout, stderr }; - })(); + + return new Promise((resolve, reject) => { + const stdoutChunks: Buffer[] = []; + const stderrChunks: Buffer[] = []; + + this.childProcess.stdout!.on("data", (chunk: Buffer) => { + stdoutChunks.push(chunk); + }); + + this.childProcess.stderr!.on("data", (chunk: Buffer) => { + stderrChunks.push(chunk); + }); + + this.childProcess.on("close", (code) => { + const stdoutBuf = Buffer.concat(stdoutChunks); + const stderrBuf = Buffer.concat(stderrChunks); + + span?.setAttribute("outputBytes", stdoutBuf.byteLength); + + withSpan("decodeResponse", () => { + const stderr = stderrBuf.byteLength > 0 + ? stderrBuf.toString() + : undefined; + if (stderr) { + console.warn(stderr); + } + if (code !== 0) { + span?.setStatus({ code: SpanStatusCode.ERROR, message: stderr }); + log.error(`Error: ${stderr}`, "schema:sync"); + reject(new Error(stderr)); + return { stdout: "", stderr }; + } + log.info( + `Dumped schema. bytes=${stdoutBuf.byteLength}`, + "schema:sync", + ); + const stdout = stdoutBuf.toString(); + resolve({ stdout, stderr }); + return { stdout, stderr }; + })(); + }); + + this.childProcess.on("error", reject); + }); } async pipeTo(restore: RestoreCommand): Promise { - // Start consuming stderr in the background to prevent resource leaks - // const stderrPromise = this.process.stderr.text(); - - const decoder = new TextDecoder(); - this.process.stderr.pipeTo( - new WritableStream({ - write: (chunk) => { - this.emit("dump", decoder.decode(chunk)); - }, - }), - ); - - restore.stderr.pipeTo( - new WritableStream({ - write: (chunk) => { - this.emit("restore", decoder.decode(chunk)); - }, - }), - ); - restore.stdout.pipeTo( - new WritableStream({ - write: (chunk) => { - this.emit("restore", decoder.decode(chunk)); - }, - }), - ); - - try { - await this.process.stdout.pipeTo(restore.stdin); - } catch (_error) { - return { - dump: { - status: await this.process.status, - }, - }; - } + this.childProcess.stderr!.on("data", (chunk: Buffer) => { + this.emit("dump", chunk.toString()); + }); + + restore.stderr.on("data", (chunk: Buffer) => { + this.emit("restore", chunk.toString()); + }); + restore.stdout.on("data", (chunk: Buffer) => { + this.emit("restore", chunk.toString()); + }); - const dumpStatus = await this.process.status; - // this only fails if the command is non-zero - const restoreStatus = await restore.status; - const out = { - dump: { - status: dumpStatus, - }, - restore: { - status: restoreStatus, - }, + this.childProcess.stdout!.pipe(restore.stdin); + + const [dumpStatus, restoreStatus] = await Promise.all([ + waitForExit(this.childProcess), + waitForExit(restore.childProcess), + ]); + + return { + dump: { status: dumpStatus }, + restore: { status: restoreStatus }, }; - await restore.cleanup(); - return out; } /** @@ -269,7 +265,11 @@ export type DumpCommandOutput = { */ export class RestoreCommand { public static readonly binaryPath = findPgRestoreBinary("17.2"); - private constructor(private process: Deno.ChildProcess) {} + readonly childProcess: ChildProcess; + + private constructor(childProcess: ChildProcess) { + this.childProcess = childProcess; + } static spawn(connectable: Connectable): RestoreCommand { const args = [ @@ -282,42 +282,33 @@ export class RestoreCommand { connectable.toString(), ]; - const command = new Deno.Command(RestoreCommand.binaryPath, { - args, - stdin: "piped", - stdout: "piped", - stderr: "piped", + const childProcess = spawn(RestoreCommand.binaryPath, args, { + stdio: ["pipe", "pipe", "pipe"], signal: shutdownController.signal, }); - const process = command.spawn(); - - return new RestoreCommand(process); + return new RestoreCommand(childProcess); } get stdin() { - return this.process.stdin; + return this.childProcess.stdin!; } get stdout() { - return this.process.stdout; + return this.childProcess.stdout!; } get stderr() { - return this.process.stderr; + return this.childProcess.stderr!; } - get status() { - return this.process.status; + get status(): Promise { + return waitForExit(this.childProcess); } async cleanup() { - if (!this.process.stdout.locked) { - await this.process.stdout.cancel(); - } - if (!this.process.stderr.locked) { - await this.process.stderr.cancel(); - } + this.childProcess.stdout?.destroy(); + this.childProcess.stderr?.destroy(); } private static formatFlags(): string[] { @@ -327,9 +318,21 @@ export class RestoreCommand { export type RestoreCommandResult = { dump: { - status: Deno.CommandStatus; + status: CommandStatus; }; restore?: { - status: Deno.CommandStatus; + status: CommandStatus; }; }; + +function waitForExit(child: ChildProcess): Promise { + return new Promise((resolve) => { + if (child.exitCode !== null) { + resolve({ code: child.exitCode, success: child.exitCode === 0 }); + return; + } + child.on("close", (code) => { + resolve({ code, success: code === 0 }); + }); + }); +} diff --git a/src/sync/sync.test.ts b/src/sync/sync.test.ts index 2deb81b..823c4e8 100644 --- a/src/sync/sync.test.ts +++ b/src/sync/sync.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "@std/assert"; +import { test, expect } from "vitest"; import { DatabaseConnector, DependencyAnalyzer, @@ -59,7 +59,7 @@ function testDb(): DatabaseConnector<{ }; } -Deno.test(async function addTest() { +test("addTest", async () => { const dbSimple = testDb(); const da = new DependencyAnalyzer(dbSimple, { requiredRows: 2, @@ -70,7 +70,7 @@ Deno.test(async function addTest() { await dbSimple.dependencies({ excludedSchemas: [] }), ); const result = await da.findAllDependencies(graph); - assertEquals(result.items, { + expect(result.items).toEqual({ "public.posts": [ { id: 3, poster_id: 0 }, { id: 4, poster_id: 1 }, diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ad003e0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2024"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "declaration": true, + "sourceMap": true, + "allowImportingTsExtensions": true, + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..6f7984b --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["src/**/*.test.ts"], + testTimeout: 120_000, + }, +}); From df59ac5c5efd29b9cc8edaa9563eeb4603814179 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Mon, 16 Feb 2026 20:30:53 +0400 Subject: [PATCH 3/8] ci(analyzer): update CI/CD and Docker for Node.js Co-Authored-By: Claude Opus 4.6 --- .dockerignore | 5 +++ .github/workflows/build-action.yaml | 18 ++++---- .github/workflows/publish.yaml | 4 +- Dockerfile | 65 +++++++++++------------------ action.yaml | 17 ++++---- 5 files changed, 52 insertions(+), 57 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..eb82b74 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +dist +.git +.idea +coverage diff --git a/.github/workflows/build-action.yaml b/.github/workflows/build-action.yaml index 1ef7961..8ac2db8 100644 --- a/.github/workflows/build-action.yaml +++ b/.github/workflows/build-action.yaml @@ -14,16 +14,18 @@ jobs: with: fetch-depth: 0 fetch-tags: true - - name: Install Deno - uses: denoland/setup-deno@v2 + - name: Install Node.js + uses: actions/setup-node@v6 with: - cache-hash: ${{ hashFiles('**/deno.lock') }} - deno-version: v2.x + node-version: 24 + cache: npm + - name: Install dependencies + run: npm ci - name: Export version run: | - echo "DENO_VERSION=$(jq -r '.version' deno.json)" >> $GITHUB_ENV + echo "VERSION=$(jq -r '.version' package.json)" >> $GITHUB_ENV - name: Typecheck - run: deno check + run: npx tsc --noEmit - name: Install PostgreSQL 17 client run: | @@ -33,7 +35,7 @@ jobs: sudo apt install -y postgresql-client-17 - name: Run tests - run: deno run test + run: npx vitest run env: PG_DUMP_BINARY: /usr/lib/postgresql/17/bin/pg_dump PG_RESTORE_BINARY: /usr/lib/postgresql/17/bin/pg_restore @@ -42,6 +44,6 @@ jobs: uses: softprops/action-gh-release@v2 if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} with: - tag_name: v${{ env.DENO_VERSION }} + tag_name: v${{ env.VERSION }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index efe4046..15d3cbb 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -40,9 +40,9 @@ jobs: key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx- - - name: Set sync_version from deno.json + - name: Set sync_version from package.json run: | - sync_version=$(jq -r '.version' deno.json) + sync_version=$(jq -r '.version' package.json) echo "sync_version=${sync_version}" >> $GITHUB_ENV - name: Build and push @query-doctor/analyzer id: build diff --git a/Dockerfile b/Dockerfile index 05aee50..a0cc975 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,11 @@ ARG ALPINE_VERSION=3.22 -ARG DENO_VERSION=2.4.5 -ARG PG_IMAGE=ghcr.io/query-doctor/postgres:pg14-timescale-2.16 - -FROM denoland/deno:alpine-${DENO_VERSION} AS deno +ARG PG_IMAGE=pg14-timescale-2.16 +FROM alpine:${ALPINE_VERSION} AS pgbadger-builder RUN apk add --no-cache \ perl \ curl \ - make \ - postgresql-client \ - git + make ARG PGBADGER_VERSION=13.2 WORKDIR /tmp @@ -22,34 +18,20 @@ RUN curl -L https://github.com/darold/pgbadger/archive/v${PGBADGER_VERSION}.tar. make install && \ rm -rf /tmp/pgbadger* -FROM gcr.io/distroless/cc-debian12:latest AS cc - -FROM alpine:${ALPINE_VERSION} AS sym - -COPY --from=cc --chmod=755 --chown=root:root /lib/*-linux-gnu/ld-linux-* /usr/local/lib/ -RUN mkdir -p -m 755 /tmp/lib -RUN ln -s /usr/local/lib/ld-linux-* /tmp/lib/ - -FROM denoland/deno:alpine-${DENO_VERSION} AS build - -COPY deno.json deno.lock* ./ -RUN deno install --frozen-lockfile +# Build the application +FROM node:24-alpine AS build +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci COPY . . +RUN npm run build +RUN npm ci --omit=dev -RUN deno compile \ - --allow-run \ - --allow-read \ - --allow-write \ - --allow-env \ - --allow-net \ - --allow-sys \ - -o /app/analyzer \ - src/main.ts - -FROM alpine:${ALPINE_VERSION} +# Final image ARG PG_IMAGE ENV LD_LIBRARY_PATH="/usr/local/lib" +FROM node:24-alpine RUN apk add -uU --no-cache \ readline \ @@ -57,17 +39,20 @@ RUN apk add -uU --no-cache \ bash \ su-exec \ openssl \ - ossp-uuid \ + krb5 \ postgresql-client \ - krb5 + perl -COPY --from=build --chmod=755 --chown=root:root /app/analyzer /app/analyzer -COPY --from=cc --chmod=755 --chown=root:root /lib/*-linux-gnu/* /usr/local/lib/ -COPY --from=sym --chmod=755 --chown=root:root /tmp/lib /lib -COPY --from=sym --chmod=755 --chown=root:root /tmp/lib /lib64 +# Copy pgBadger +COPY --from=pgbadger-builder /usr/local/bin/pgbadger /usr/local/bin/pgbadger COPY --from=ghcr.io/query-doctor/postgres:pg14-timescale-2.16 /usr/local/pgsql /usr/local/pgsql +# Copy application +COPY --from=build /app/dist /app/dist +COPY --from=build /app/node_modules /app/node_modules + +# Setup postgres user and directories RUN mkdir -p /var/lib/postgresql/data \ && chown -R postgres:postgres /var/lib/postgresql \ && chown -R postgres:postgres /usr/local/pgsql \ @@ -75,8 +60,8 @@ RUN mkdir -p /var/lib/postgresql/data \ WORKDIR /app # making sure we use the binaries from the installed postgresql17 client -ENV PG_DUMP_BINARY=/usr/bin/pg_dump -ENV PG_RESTORE_BINARY=/usr/bin/pg_restore +ENV PG_DUMP_BINARY=/usr/local/pgsql/bin/pg_dump +ENV PG_RESTORE_BINARY=/usr/local/pgsql/bin/pg_restore ENV PATH="/usr/local/pgsql/bin:$PATH" ENV PGDATA=/var/lib/postgresql/data @@ -85,7 +70,7 @@ RUN sed -i 's|nobody:/|nobody:/home|' /etc/passwd && chown nobody:nobody /home ENV POSTGRES_URL=postgresql://postgres@localhost/postgres?host=/tmp RUN su-exec postgres initdb -D $PGDATA || true && \ - echo "shared_preload_libraries = 'timescaledb,pg_stat_statements'" >> $PGDATA/postgresql.conf && \ + # echo "shared_preload_libraries = 'timescaledb,pg_stat_statements'" >> $PGDATA/postgresql.conf && \ echo "listen_addresses = ''" >> $PGDATA/postgresql.conf && \ echo "unix_socket_directories = '/tmp'" >> $PGDATA/postgresql.conf @@ -96,4 +81,4 @@ EXPOSE 2345 CMD ["/bin/bash", "-c", "\ pg_ctl -D $PGDATA -l $PGDATA/logfile start || (cat $PGDATA/logfile && exit 1) && \ until pg_isready -h /tmp; do sleep 0.5; done && \ - /app/analyzer"] + node /app/dist/main.mjs"] diff --git a/action.yaml b/action.yaml index e1c71df..11e69b0 100644 --- a/action.yaml +++ b/action.yaml @@ -11,12 +11,15 @@ branding: runs: using: "composite" steps: - # Setup Deno environment - - name: Install Deno - uses: denoland/setup-deno@v2 + - name: Install Node.js + uses: actions/setup-node@v6 with: - cache: true - deno-version: v2.x + node-version: 24 + cache: npm + + - name: Install dependencies + shell: bash + run: npm ci # Cache pgBadger build - name: Cache pgBadger @@ -48,10 +51,10 @@ runs: sudo make install # Use sudo to install globally cd ${{ github.action_path }} # Return to action directory - # Run the compiled application + # Run the application - name: Run Analyzer shell: bash - run: deno run start + run: npm run start env: PG_DUMP_BINARY: /usr/bin/pg_dump CI: "true" From fa8fcb1cff8bcc1b613baa0c08916bbeb0d025a7 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Mon, 16 Feb 2026 22:28:43 +0400 Subject: [PATCH 4/8] test: add assert/assertDefined helpers to eliminate type casts - assert() with `asserts condition` narrows discriminated unions, matching Deno's assert() behavior. Removes all `as any` from optimization property access in query-optimizer tests. - assertDefined() narrows nullable values, removing non-null assertions. Co-Authored-By: Claude Opus 4.6 --- src/remote/gin-indexes.test.ts | 53 ++++++++++---------- src/remote/query-optimizer.test.ts | 77 +++++++++++++++--------------- src/remote/test-utils.ts | 15 ++++++ 3 files changed, 81 insertions(+), 64 deletions(-) create mode 100644 src/remote/test-utils.ts diff --git a/src/remote/gin-indexes.test.ts b/src/remote/gin-indexes.test.ts index 41ec425..97ba728 100644 --- a/src/remote/gin-indexes.test.ts +++ b/src/remote/gin-indexes.test.ts @@ -1,4 +1,5 @@ import { test, expect, vi, afterEach } from "vitest"; +import { assertDefined } from "./test-utils.ts"; import { PostgreSqlContainer } from "@testcontainers/postgresql"; import { QueryOptimizer } from "./query-optimizer.ts"; import { ConnectionManager } from "../sync/connection-manager.ts"; @@ -96,10 +97,10 @@ test("GIN: basic @> containment recommends GIN with jsonb_path_ops", async () => const match = improvements.find((q) => q.query.includes("@>") || q.query.includes("products") ); - expect(match, "Expected improvements for @> containment query").toBeTruthy(); - expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + assertDefined(match, "Expected improvements for @> containment query"); + expect(hasGinRecommendation(match), "Expected a GIN index recommendation").toBeTruthy(); - const ginRecs = getGinRecommendations(match!); + const ginRecs = getGinRecommendations(match); expect( ginRecs.some((r) => r.definition.toLowerCase().includes("jsonb_path_ops")), `Expected jsonb_path_ops, got: ${ginRecs.map((r) => r.definition)}`, @@ -181,10 +182,10 @@ test("GIN: key existence (?) recommends GIN with default jsonb_ops", async () => const match = improvements.find((q) => q.query.includes("?") || q.query.includes("payload") ); - expect(match, "Expected improvements for ? key existence query").toBeTruthy(); - expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + assertDefined(match, "Expected improvements for ? key existence query"); + expect(hasGinRecommendation(match), "Expected a GIN index recommendation").toBeTruthy(); - const ginRecs = getGinRecommendations(match!); + const ginRecs = getGinRecommendations(match); // ? requires jsonb_ops — must NOT have jsonb_path_ops expect( ginRecs.every((r) => !r.definition.toLowerCase().includes("jsonb_path_ops")), @@ -268,10 +269,10 @@ test("GIN: any-key existence (?|) recommends GIN with default jsonb_ops", async const match = improvements.find((q) => q.query.includes("?|") || q.query.includes("payload") ); - expect(match, "Expected improvements for ?| any-key existence query").toBeTruthy(); - expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + assertDefined(match, "Expected improvements for ?| any-key existence query"); + expect(hasGinRecommendation(match), "Expected a GIN index recommendation").toBeTruthy(); - const ginRecs = getGinRecommendations(match!); + const ginRecs = getGinRecommendations(match); expect( ginRecs.every((r) => !r.definition.toLowerCase().includes("jsonb_path_ops")), `Expected default jsonb_ops for ?| operator, got: ${ginRecs.map((r) => r.definition)}`, @@ -352,10 +353,10 @@ test("GIN: all-keys existence (?&) recommends GIN with default jsonb_ops", async const match = improvements.find((q) => q.query.includes("?&") || q.query.includes("payload") ); - expect(match, "Expected improvements for ?& all-keys existence query").toBeTruthy(); - expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + assertDefined(match, "Expected improvements for ?& all-keys existence query"); + expect(hasGinRecommendation(match), "Expected a GIN index recommendation").toBeTruthy(); - const ginRecs = getGinRecommendations(match!); + const ginRecs = getGinRecommendations(match); expect( ginRecs.every((r) => !r.definition.toLowerCase().includes("jsonb_path_ops")), `Expected default jsonb_ops for ?& operator, got: ${ginRecs.map((r) => r.definition)}`, @@ -439,19 +440,19 @@ test("GIN: mixed JSONB and regular column produces both GIN and B-tree", async ( const match = improvements.find((q) => q.query.includes("products") ); - expect(match, "Expected improvements for mixed JSONB + regular query").toBeTruthy(); + assertDefined(match, "Expected improvements for mixed JSONB + regular query"); expect( - match!.optimization.state, - `Expected improvements_available but got ${match!.optimization.state}`, + match.optimization.state, + `Expected improvements_available but got ${match.optimization.state}`, ).toEqual("improvements_available"); - const ginRecs = getGinRecommendations(match!); - const btreeRecs = getBtreeRecommendations(match!); + const ginRecs = getGinRecommendations(match); + const btreeRecs = getBtreeRecommendations(match); // Should have a GIN recommendation for the JSONB column expect( ginRecs.length, - `Expected GIN recommendation for data column, got: ${JSON.stringify(match!.optimization.state === "improvements_available" ? match!.optimization.indexRecommendations.map((r) => r.definition) : [])}`, + `Expected GIN recommendation for data column, got: ${JSON.stringify(match.optimization.state === "improvements_available" ? match.optimization.indexRecommendations.map((r) => r.definition) : [])}`, ).toBeGreaterThan(0); // The two index types should not interfere — GIN for data, B-tree for price // The optimizer may or may not also produce a B-tree for price depending on @@ -546,9 +547,9 @@ test("GIN: mixed @> and ? on same column escalates to jsonb_ops", async () => { const match = improvements.find((q) => q.query.includes("products") ); - expect(match, "Expected improvements for mixed @> and ? query").toBeTruthy(); + assertDefined(match, "Expected improvements for mixed @> and ? query"); - const ginRecs = getGinRecommendations(match!); + const ginRecs = getGinRecommendations(match); // Should produce exactly ONE GIN index, not two expect( @@ -636,10 +637,10 @@ test("GIN: table alias resolves to correct table for GIN recommendation", async const match = improvements.find((q) => q.query.includes("products") ); - expect(match, "Expected improvements for aliased JSONB query").toBeTruthy(); - expect(hasGinRecommendation(match!), "Expected a GIN index recommendation").toBeTruthy(); + assertDefined(match, "Expected improvements for aliased JSONB query"); + expect(hasGinRecommendation(match), "Expected a GIN index recommendation").toBeTruthy(); - const ginRecs = getGinRecommendations(match!); + const ginRecs = getGinRecommendations(match); // Should target the real table "products", not the alias "p" expect( ginRecs.some((r) => r.table === "products"), @@ -718,15 +719,15 @@ test("GIN: non-JSONB query produces B-tree only, no GIN", async () => { await optimizer.finish; const match = improvements.find((q) => q.query.includes("users")); - expect(match, "Expected improvements for non-JSONB equality query").toBeTruthy(); + assertDefined(match, "Expected improvements for non-JSONB equality query"); - const ginRecs = getGinRecommendations(match!); + const ginRecs = getGinRecommendations(match); expect( ginRecs.length, `Expected no GIN recommendations for non-JSONB query, got: ${ginRecs.map((r) => r.definition)}`, ).toEqual(0); - const btreeRecs = getBtreeRecommendations(match!); + const btreeRecs = getBtreeRecommendations(match); expect( btreeRecs.length, "Expected B-tree recommendation for text equality query", diff --git a/src/remote/query-optimizer.test.ts b/src/remote/query-optimizer.test.ts index 5ac2687..058e9d5 100644 --- a/src/remote/query-optimizer.test.ts +++ b/src/remote/query-optimizer.test.ts @@ -1,4 +1,5 @@ import { test, expect, vi, afterEach } from "vitest"; +import { assert, assertDefined } from "./test-utils.ts"; import { PostgreSqlContainer } from "@testcontainers/postgresql"; import { QueryOptimizer } from "./query-optimizer.ts"; import { ConnectionManager } from "../sync/connection-manager.ts"; @@ -239,22 +240,22 @@ test("disabling an index removes it from indexesUsed and recommends it", async ( const emailQuery = recentQueries.find((q) => q.query.includes("email") && q.query.includes("users") ); - expect(emailQuery, "Expected to find email query in recent queries").toBeTruthy(); + assertDefined(emailQuery, "Expected to find email query in recent queries"); - await optimizer.start([emailQuery!], statsMode); + await optimizer.start([emailQuery], statsMode); await optimizer.finish; const queriesAfterFirstRun = optimizer.getQueries(); const emailQueryResult = queriesAfterFirstRun.find((q) => q.query.includes("email") ); - expect(emailQueryResult, "Expected email query in results").toBeTruthy(); - expect( - emailQueryResult!.optimization.state, - `Expected no_improvement_found but got ${emailQueryResult!.optimization.state}`, - ).toEqual("no_improvement_found"); - expect((emailQueryResult!.optimization as any).indexesUsed).toEqual(expect.arrayContaining(["users_email_idx"])); - const costWithIndex = (emailQueryResult!.optimization as any).cost; + assertDefined(emailQueryResult, "Expected email query in results"); + assert( + emailQueryResult.optimization.state === "no_improvement_found", + `Expected no_improvement_found but got ${emailQueryResult.optimization.state}`, + ); + expect(emailQueryResult.optimization.indexesUsed).toEqual(expect.arrayContaining(["users_email_idx"])); + const costWithIndex = emailQueryResult.optimization.cost; const { PgIdentifier } = await import("@query-doctor/core"); optimizer.toggleIndex(PgIdentifier.fromString("users_email_idx")); @@ -264,33 +265,33 @@ test("disabling an index removes it from indexesUsed and recommends it", async ( "Expected users_email_idx to be disabled", ).toBeTruthy(); - await optimizer.addQueries([emailQuery!]); + await optimizer.addQueries([emailQuery]); await optimizer.finish; const queriesAfterToggle = optimizer.getQueries(); const emailQueryAfterToggle = queriesAfterToggle.find((q) => q.query.includes("email") ); - expect(emailQueryAfterToggle, "Expected email query after toggle").toBeTruthy(); - expect( - emailQueryAfterToggle!.optimization.state, - `Expected improvements_available after toggle but got ${emailQueryAfterToggle!.optimization.state}`, - ).toEqual("improvements_available"); - expect((emailQueryAfterToggle!.optimization as any).indexesUsed).not.toContain("users_email_idx"); - expect((emailQueryAfterToggle!.optimization as any).cost).toBeGreaterThan(costWithIndex); + assertDefined(emailQueryAfterToggle, "Expected email query after toggle"); + assert( + emailQueryAfterToggle.optimization.state === "improvements_available", + `Expected improvements_available after toggle but got ${emailQueryAfterToggle.optimization.state}`, + ); + expect(emailQueryAfterToggle.optimization.indexesUsed).not.toContain("users_email_idx"); + expect(emailQueryAfterToggle.optimization.cost).toBeGreaterThan(costWithIndex); const recommendations = - (emailQueryAfterToggle!.optimization as any).indexRecommendations; + emailQueryAfterToggle.optimization.indexRecommendations; expect( - recommendations.some((r: any) => - r.columns.some((c: any) => c.column === "email") + recommendations.some((r) => + r.columns.some((c) => c.column === "email") ), "Expected recommendation for email column after disabling the index", ).toBeTruthy(); // Verify explainPlan doesn't show the disabled index being used const explainPlanAfterToggle = - (emailQueryAfterToggle!.optimization as any).explainPlan; - expect(explainPlanAfterToggle, "Expected explainPlan to be present").toBeTruthy(); + emailQueryAfterToggle.optimization.explainPlan; + assertDefined(explainPlanAfterToggle, "Expected explainPlan to be present"); const explainStr = JSON.stringify(explainPlanAfterToggle); expect(explainStr).not.toContain("users_email_idx"); } finally { @@ -477,9 +478,9 @@ test("timed out queries are retried with exponential backoff up to maxRetries", const slowQuery = recentQueries.find((q) => q.query.includes("slow_table") && q.query.startsWith("select") ); - expect(slowQuery, "Expected to find slow_table query").toBeTruthy(); + assertDefined(slowQuery, "Expected to find slow_table query"); - await optimizer.start([slowQuery!], { + await optimizer.start([slowQuery], { kind: "fromStatisticsExport", source: { kind: "inline" }, stats: [{ @@ -500,16 +501,16 @@ test("timed out queries are retried with exponential backoff up to maxRetries", const queries = optimizer.getQueries(); const resultQuery = queries.find((q) => q.query.includes("slow_table")); - expect(resultQuery, "Expected slow_table query in results").toBeTruthy(); + assertDefined(resultQuery, "Expected slow_table query in results"); expect( - resultQuery!.optimization.state, + resultQuery.optimization.state, "Expected query to be in timeout state", ).toEqual("timeout"); - if (resultQuery!.optimization.state === "timeout") { + if (resultQuery.optimization.state === "timeout") { expect( - resultQuery!.optimization.retries, + resultQuery.optimization.retries, `Expected ${maxRetries} retries`, ).toEqual(maxRetries); } @@ -564,9 +565,9 @@ test("optimizer does not treat ASC index as duplicate of DESC candidate", async const mixedQuery = recentQueries.find((q) => q.query.includes("order by created_at desc, status asc") ); - expect(mixedQuery, "Expected to find mixed sort direction query").toBeTruthy(); + assertDefined(mixedQuery, "Expected to find mixed sort direction query"); - await optimizer.start([mixedQuery!], { + await optimizer.start([mixedQuery], { kind: "fromStatisticsExport", source: { kind: "inline" }, stats: [{ @@ -595,16 +596,16 @@ test("optimizer does not treat ASC index as duplicate of DESC candidate", async const result = queries.find((q) => q.query.includes("order by created_at desc, status asc") ); - expect(result, "Expected to find query result").toBeTruthy(); - expect( - result!.optimization.state, - `Expected improvements_available (ASC,ASC can't satisfy DESC,ASC via backward scan). Got: ${result!.optimization.state}`, - ).toEqual("improvements_available"); + assertDefined(result, "Expected to find query result"); + assert( + result.optimization.state === "improvements_available", + `Expected improvements_available (ASC,ASC can't satisfy DESC,ASC via backward scan). Got: ${result.optimization.state}`, + ); - const recommendations = (result!.optimization as any).indexRecommendations; - const hasMixedRecommendation = recommendations.some((r: any) => + const recommendations = result.optimization.indexRecommendations; + const hasMixedRecommendation = recommendations.some((r) => r.columns.length >= 2 && - r.columns.some((c: any) => c.column === "created_at" && c.sort?.dir === "SORTBY_DESC") + r.columns.some((c) => c.column === "created_at" && c.sort?.dir === "SORTBY_DESC") ); expect( hasMixedRecommendation, diff --git a/src/remote/test-utils.ts b/src/remote/test-utils.ts new file mode 100644 index 0000000..acffc21 --- /dev/null +++ b/src/remote/test-utils.ts @@ -0,0 +1,15 @@ +import { expect } from "vitest"; + +export function assert( + condition: unknown, + message?: string, +): asserts condition { + expect(condition, message ?? "Assertion failed").toBeTruthy(); +} + +export function assertDefined( + value: T, + message?: string, +): asserts value is NonNullable { + expect(value, message ?? "Expected value to be defined").toBeTruthy(); +} From c87f8694691f89d71a0c3fe96f47bd0261521dfe Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Mon, 16 Feb 2026 22:28:47 +0400 Subject: [PATCH 5/8] chore: annotate untyped signatures introduced by migration Co-Authored-By: Claude Opus 4.6 --- src/remote/remote-controller.test.ts | 2 ++ src/remote/remote-controller.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/remote/remote-controller.test.ts b/src/remote/remote-controller.test.ts index e7cbc3e..cbf6c22 100644 --- a/src/remote/remote-controller.test.ts +++ b/src/remote/remote-controller.test.ts @@ -111,6 +111,7 @@ test("creating an index via endpoint adds it to the optimizing db", async () => }); expect(createResult.status).toEqual(200); + // as any: HandlerResult.body is unknown — will be typed (Site#2402) expect((createResult.body as any).success).toEqual(true); // Verify the index was created on the optimizing db @@ -157,6 +158,7 @@ test("controller returns extension error when pg_stat_statements is not installe expect(syncResult.status).toEqual(200); + // as any: HandlerResult.body is unknown — will be typed (Site#2402) const body = syncResult.body as any; // Schema should still sync successfully expect(body.schema.type).toEqual("ok"); diff --git a/src/remote/remote-controller.ts b/src/remote/remote-controller.ts index cb8819c..f9f9f71 100644 --- a/src/remote/remote-controller.ts +++ b/src/remote/remote-controller.ts @@ -22,6 +22,7 @@ type SyncStatus = typeof SyncStatus[keyof typeof SyncStatus]; type HandlerResult = { status: number; + // TODO: type body per route (Site#2402) body: unknown; }; @@ -52,6 +53,7 @@ export class RemoteController { remote.on("restoreLog", this.makeLoggingHandler("pg_restore").bind(this)); } + // TODO: type body param (Site#2402) async toggleIndex(body: unknown): Promise { try { const index = ToggleIndexDto.parse(body); @@ -79,6 +81,7 @@ export class RemoteController { } } + // TODO: type return (Site#2402) async getStatus(): Promise { if (!this.syncResponse || this.syncStatus !== SyncStatus.COMPLETED) { return { status: this.syncStatus }; From 014811043875f90bb8ce79dcad1529db13561716 Mon Sep 17 00:00:00 2001 From: Xetera Date: Tue, 17 Feb 2026 20:52:11 +0300 Subject: [PATCH 6/8] fix: dockerfile build --- Dockerfile | 23 ++++++++++++----------- docker-entrypoint.sh | 11 +++++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile index a0cc975..d4e9618 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,7 @@ RUN apk add -uU --no-cache \ su-exec \ openssl \ krb5 \ - postgresql-client \ + postgresql17-client \ perl # Copy pgBadger @@ -60,8 +60,8 @@ RUN mkdir -p /var/lib/postgresql/data \ WORKDIR /app # making sure we use the binaries from the installed postgresql17 client -ENV PG_DUMP_BINARY=/usr/local/pgsql/bin/pg_dump -ENV PG_RESTORE_BINARY=/usr/local/pgsql/bin/pg_restore +ENV PG_DUMP_BINARY=/usr/bin/pg_dump +ENV PG_RESTORE_BINARY=/usr/bin/pg_restore ENV PATH="/usr/local/pgsql/bin:$PATH" ENV PGDATA=/var/lib/postgresql/data @@ -69,16 +69,17 @@ RUN sed -i 's|nobody:/|nobody:/home|' /etc/passwd && chown nobody:nobody /home ENV POSTGRES_URL=postgresql://postgres@localhost/postgres?host=/tmp -RUN su-exec postgres initdb -D $PGDATA || true && \ - # echo "shared_preload_libraries = 'timescaledb,pg_stat_statements'" >> $PGDATA/postgresql.conf && \ - echo "listen_addresses = ''" >> $PGDATA/postgresql.conf && \ - echo "unix_socket_directories = '/tmp'" >> $PGDATA/postgresql.conf +COPY ./docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + USER postgres +RUN initdb -D "$PGDATA" + +# We don't expose 5432 because +# 1. We use a unix socket +# 2. The external user should never have to interface with the internal postgres service EXPOSE 2345 -CMD ["/bin/bash", "-c", "\ - pg_ctl -D $PGDATA -l $PGDATA/logfile start || (cat $PGDATA/logfile && exit 1) && \ - until pg_isready -h /tmp; do sleep 0.5; done && \ - node /app/dist/main.mjs"] +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..b942b8b --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +echo "listen_addresses = '0.0.0.0'" >> "$PGDATA/postgresql.conf" +echo "host all all 0.0.0.0/0 trust" >> "$PGDATA/pg_hba.conf" +echo "shared_preload_libraries = 'timescaledb,pg_stat_statements'" >> "$PGDATA/postgresql.conf" + +pg_ctl -D "$PGDATA" -l "$PGDATA/logfile" start || (cat "$PGDATA/logfile" && exit 1) + +until pg_isready -h /tmp; do sleep 0.5; done + +node /app/dist/main.mjs From ba85a40507a313887d734224e12e99c75251ac0b Mon Sep 17 00:00:00 2001 From: Xetera Date: Tue, 17 Feb 2026 22:11:59 +0300 Subject: [PATCH 7/8] feat: remove `toPgTextArray` --- src/sql/postgresjs.ts | 10 ---------- src/sync/pg-connector.ts | 5 ++--- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/sql/postgresjs.ts b/src/sql/postgresjs.ts index 94714eb..8ffa5b6 100644 --- a/src/sql/postgresjs.ts +++ b/src/sql/postgresjs.ts @@ -84,16 +84,6 @@ function serializeParams(params?: unknown[]): unknown[] | undefined { }); } -/** - * Format a string array as a PostgreSQL array literal. - * Use this for params that target ::text[] casts instead of ::jsonb, - * so they bypass JSON serialization in serializeParams. - */ -export function toPgTextArray(arr: string[]): string { - if (arr.length === 0) return "{}"; - return "{" + arr.map((s) => '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"').join(",") + "}"; -} - export function wrapPgPool(pool: Pool): Postgres { // Handle idle client errors to prevent process crashes. // Expected during DROP DATABASE ... WITH (FORCE) which terminates diff --git a/src/sync/pg-connector.ts b/src/sync/pg-connector.ts index a412984..8ebbb14 100644 --- a/src/sync/pg-connector.ts +++ b/src/sync/pg-connector.ts @@ -23,7 +23,6 @@ import { SegmentedQueryCache } from "./seen-cache.ts"; import { FullSchema, FullSchemaColumn } from "./schema_differ.ts"; import { ExtensionNotInstalledError, PostgresError } from "./errors.ts"; import { RawRecentQuery, RecentQuery } from "../sql/recent-query.ts"; -import { toPgTextArray } from "../sql/postgresjs.ts"; import { ConnectionManager } from "./connection-manager.ts"; const __filename = fileURLToPath(import.meta.url); @@ -185,7 +184,7 @@ ORDER BY pg_tables.tablename, fk."referencedTable", fk."sourceColumn";-- @qd_introspection `, [ - toPgTextArray(options.excludedSchemas), + options.excludedSchemas, ], ), )(); @@ -445,7 +444,7 @@ ORDER BY JOIN unnest($1::text[], $2::text[]) AS t(schema_name, table_name) ON n.nspname = t.schema_name AND c.relname = t.table_name WHERE c.relkind IN ('r', 'm')`, - [toPgTextArray(schemaNames), toPgTextArray(tableNames)], + [schemaNames, tableNames], ); return Number(results[0]?.total_rows ?? 0); } From 8fbcec26b25c891c9bd665c27914fe483af4b3a3 Mon Sep 17 00:00:00 2001 From: Xetera Date: Tue, 17 Feb 2026 22:12:28 +0300 Subject: [PATCH 8/8] fix: tweak custom serialization logic --- src/sql/postgresjs.ts | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/sql/postgresjs.ts b/src/sql/postgresjs.ts index 8ffa5b6..8cbbbce 100644 --- a/src/sql/postgresjs.ts +++ b/src/sql/postgresjs.ts @@ -1,4 +1,4 @@ -import { Pool, type PoolConfig, type PoolClient } from "pg"; +import { Pool, type PoolConfig } from "pg"; import Cursor from "pg-cursor"; import { type Postgres, @@ -69,17 +69,32 @@ function connect(connectable: Connectable, config: PoolConfig) { } /** - * Pre-serialize array/object params as JSON strings. - * pg serializes arrays as PostgreSQL array literals ({...}), - * but the Postgres interface expects postgres.js semantics - * where complex types are sent as JSON strings ([...]). + * Arrays are supported BUT only for primitive values. + * For anything else jsonb has to be serialized + */ +function serializeArray(arr: unknown[]): unknown[] | string { + const allPrimitiveValues = arr.every((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean"); + if (allPrimitiveValues) { + return arr; + } + return JSON.stringify(arr); +} + +/** + * node-postgres does not serialize jsonb in an expected way */ function serializeParams(params?: unknown[]): unknown[] | undefined { if (!params) return params; return params.map((p) => { - if (p === null || p === undefined) return p; - if (Array.isArray(p)) return JSON.stringify(p); - if (typeof p === "object" && !(p instanceof Buffer)) return JSON.stringify(p); + if (p === null || p === undefined) { + return p; + } + if (Array.isArray(p) && p.length > 0) { + return serializeArray(p); + } + if (typeof p === "object" && !(p instanceof Buffer)) { + return JSON.stringify(p); + } return p; }); } @@ -126,7 +141,7 @@ export function wrapPgPool(pool: Pool): Postgres { } }; const result = queue.then(doExec, doExec); - queue = result.then(() => {}, () => {}); + queue = result.then(() => { }, () => { }); return result; }, };