From ec32325a789f9bd74dc2905371d819d245686dec Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Mon, 13 May 2024 12:44:51 -0600 Subject: [PATCH] Port slonik-dataloaders (#599) * refactor: port slonik-dataloaders * refactor: port slonik-dataloaders * refactor: port slonik-dataloaders * refactor: port slonik-dataloaders * refactor: port slonik-dataloaders * refactor test setup * add offset * refactor test setup * feat: utilize separate queries for connection loader * remove instanceof check * remove __typename from edge fields * support validation * correct linting * remove unnecessary upper() * correctly serialize Set * handle a mix of requested info * fix lock * fix lock * fix lock * wip * wip * wip --- .changeset/tame-lizards-punch.md | 5 + .github/workflows/feature.yaml | 2 +- .github/workflows/main.yaml | 2 +- package-lock.json | 2317 ++++++++++++++--- packages/slonik-dataloaders/README.md | 134 + packages/slonik-dataloaders/package.json | 46 + packages/slonik-dataloaders/src/.eslintrc | 26 + .../src/factories/createConnectionLoader.ts | 43 + .../createConnectionLoaderClass.test.ts | 492 ++++ .../factories/createConnectionLoaderClass.ts | 313 +++ .../src/factories/createListLoader.ts | 32 + .../src/factories/createNodeByIdLoader.ts | 14 + .../createNodeByIdLoaderClass.test.ts | 104 + .../factories/createNodeByIdLoaderClass.ts | 80 + packages/slonik-dataloaders/src/index.ts | 5 + packages/slonik-dataloaders/src/types.ts | 21 + .../src/utilities/fromCursor.ts | 5 + .../src/utilities/getColumnIdentifiers.ts | 15 + .../src/utilities/getRequestedFields.ts | 47 + .../src/utilities/snakeCase.ts | 36 + .../src/utilities/toCursor.ts | 5 + packages/slonik-dataloaders/tsconfig.json | 24 + .../helpers.test/createTestRunner.ts | 9 +- .../src/helpers.test/createTestRunner.ts | 9 +- 24 files changed, 3357 insertions(+), 429 deletions(-) create mode 100644 .changeset/tame-lizards-punch.md create mode 100644 packages/slonik-dataloaders/README.md create mode 100644 packages/slonik-dataloaders/package.json create mode 100644 packages/slonik-dataloaders/src/.eslintrc create mode 100644 packages/slonik-dataloaders/src/factories/createConnectionLoader.ts create mode 100644 packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.test.ts create mode 100644 packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.ts create mode 100644 packages/slonik-dataloaders/src/factories/createListLoader.ts create mode 100644 packages/slonik-dataloaders/src/factories/createNodeByIdLoader.ts create mode 100644 packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.test.ts create mode 100644 packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.ts create mode 100644 packages/slonik-dataloaders/src/index.ts create mode 100644 packages/slonik-dataloaders/src/types.ts create mode 100644 packages/slonik-dataloaders/src/utilities/fromCursor.ts create mode 100644 packages/slonik-dataloaders/src/utilities/getColumnIdentifiers.ts create mode 100644 packages/slonik-dataloaders/src/utilities/getRequestedFields.ts create mode 100644 packages/slonik-dataloaders/src/utilities/snakeCase.ts create mode 100644 packages/slonik-dataloaders/src/utilities/toCursor.ts create mode 100644 packages/slonik-dataloaders/tsconfig.json diff --git a/.changeset/tame-lizards-punch.md b/.changeset/tame-lizards-punch.md new file mode 100644 index 00000000..62a76699 --- /dev/null +++ b/.changeset/tame-lizards-punch.md @@ -0,0 +1,5 @@ +--- +"@slonik/dataloaders": minor +--- + +Porting @danielrearden's [slonik-dataloaders](https://github.com/danielrearden/slonik-dataloaders) to Slonik monorepo as @slonik/dataloaders. \ No newline at end of file diff --git a/.github/workflows/feature.yaml b/.github/workflows/feature.yaml index c1955b94..96fef9f8 100644 --- a/.github/workflows/feature.yaml +++ b/.github/workflows/feature.yaml @@ -80,7 +80,7 @@ jobs: - run: npm ci - run: npm run build --workspaces --if-present - env: - POSTGRES_DSN: postgres:postgres@localhost:5432 + POSTGRES_DSN: 'postgres://postgres:postgres@localhost:5432' TEST_ONLY: '${{ matrix.test_only }}' run: npm run test --workspaces --if-present strategy: diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index ada52756..cf4b0d60 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -88,7 +88,7 @@ jobs: - run: npm ci - run: npm run build --workspaces --if-present - env: - POSTGRES_DSN: postgres:postgres@localhost:5432 + POSTGRES_DSN: 'postgres://postgres:postgres@localhost:5432' TEST_ONLY: '${{ matrix.test_only }}' run: npm run test --workspaces --if-present strategy: diff --git a/package-lock.json b/package-lock.json index 8ed16308..e6429ce6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -701,6 +701,12 @@ "node-fetch": "^2.5.0" } }, + "node_modules/@changesets/get-github-info/node_modules/dataloader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", + "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==", + "dev": true + }, "node_modules/@changesets/get-release-plan": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.0.tgz", @@ -796,9 +802,9 @@ } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.8.0.tgz", - "integrity": "sha512-wK1qGhy6DiCj9LqGnMKutIQcMPD8J9oczM1sr3J+zmh6WggP1xkuCHu8XSxxO4Q2AOLdtcVW/4SXJ7gzT7Azbg==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.8.1.tgz", + "integrity": "sha512-zP/cC7ABk9PM6X1/itEOYa9raWrdUtUXCcUtHLnEr83HhPUHZ8vzaBgMJ176No/7EgZ4BHGXVvA0v079ukXVxw==", "dev": true, "dependencies": { "@cspell/dict-ada": "^4.0.2", @@ -858,30 +864,30 @@ } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.8.0.tgz", - "integrity": "sha512-MlyEMnTXkLJxNSXcS7j9xV/zFS4/38qOoaH0W5xWGAtDgiFSa9/NXVBnZTNWpdhjtm0UNCfIQenl/l7d3O9Luw==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.8.1.tgz", + "integrity": "sha512-HtendGGO0w1gElhSYsD4D9iKT1nMBoUP31y0Ndw3AtQRzH6I31lx6DWnrXIef1bTL4wdq7ocxgG5HGQBQ8PWfA==", "dev": true, "dependencies": { - "@cspell/cspell-types": "8.8.0" + "@cspell/cspell-types": "8.8.1" }, "engines": { "node": ">=18" } }, "node_modules/@cspell/cspell-pipe": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.8.0.tgz", - "integrity": "sha512-R9YEI8+GVa98mEMCtCHJMqX4xHNwhdHo31lGhmyUpYiuLcD9HZ96n+ExCGbnFBUXyugSHCmTA/lubdfo7CPZrw==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.8.1.tgz", + "integrity": "sha512-AMQ0s7qH71tNnrpX8ILl+OZceDHt//h/T3Yk//qvn69x1Dzfg4Saqv/qhfG1VClQVyV5F4y7tIa4UNd5X6mW4g==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@cspell/cspell-resolver": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.8.0.tgz", - "integrity": "sha512-+o1fwkE36Wi8JTnjDHdLScB99U8YtLQ7XbnEe61Hj2ES1G5TsYCZ1r7RFCw2Kzn2qrkE2mnxknKwbf9h0Db4Ng==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.8.1.tgz", + "integrity": "sha512-K5sPp05Pz3tYU9roFGILSB6OdSVYqyr4Y/NW1CxZsXgq+hmwxAJQag/RyhW6cUp/1Jhy5RKYGNy0H0u/jODx3A==", "dev": true, "dependencies": { "global-directory": "^4.0.1" @@ -891,18 +897,18 @@ } }, "node_modules/@cspell/cspell-service-bus": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.8.0.tgz", - "integrity": "sha512-BvQgBbrsXmKmaEXhXSGQPzBTM37EMk696u6+ThuJuIikP54pKJpUc7rOV1NKretxC32Mj37mS750X1YR02Z80w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.8.1.tgz", + "integrity": "sha512-dxZ/ymwP6XNMGkU5iIUVgFP2JEqEvpJZavpAerB/y5E560Agv1WuUpkZE/PMCmseoLjSiV5yQzcnLNoT5X/w7A==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@cspell/cspell-types": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.8.0.tgz", - "integrity": "sha512-CGIYttfpp0M/y4a7vfVQljeJqBcIsGYIM4iwJU+F3MQUqFqvNFeU58S27GfK5VzkMAJumsOnmJqSgm+h/g7n0Q==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.8.1.tgz", + "integrity": "sha512-JEbLN+b3XdHIpEaoZJnpPfL8iTKWraqE7x1VwG7FIQ9wjP6fCPwfNRVM0CUWEmT+85O/zvbYVOlTJn1uIOLnZw==", "dev": true, "engines": { "node": ">=18" @@ -1224,9 +1230,9 @@ "dev": true }, "node_modules/@cspell/dynamic-import": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.8.0.tgz", - "integrity": "sha512-oNe8IPaTDsbfGbGCt7Ss+g8RRI7c9zpTnp/G/eG+PuWvH8Isps4+dWE/4qhxS7e3XTyQwfD89b3H3TAPAuwstQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.8.1.tgz", + "integrity": "sha512-IyKEbSaHkw9V4Oc4JDasF96+BHKV8motBrepjLIMUjdJ152fBg8zqbvF769POdCJg0QouZVUV+h9V7zC6v45/g==", "dev": true, "dependencies": { "import-meta-resolve": "^4.1.0" @@ -1236,9 +1242,9 @@ } }, "node_modules/@cspell/strong-weak-map": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.8.0.tgz", - "integrity": "sha512-A0mkSdPiZkbF3e+OGM2eO1k0yrdgohvgO2p3fhb1bGylj8n6po+H6iNh2zpumTtfy1xVIrfpYPdOT7Z6TvvbIw==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.8.1.tgz", + "integrity": "sha512-QNnMY5jKP/ItVYRGS4w3KF+1iXBUUjldZNVtEoQe2dFergxvbIYQ0S++TZb25G/o9nRF5GmOpecJaOvwUBZsiw==", "dev": true, "engines": { "node": ">=18" @@ -1310,6 +1316,397 @@ "node": ">=16" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1476,12 +1873,6 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/batch-execute/node_modules/dataloader": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", - "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==", - "dev": true - }, "node_modules/@graphql-tools/code-file-loader": { "version": "7.3.23", "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-7.3.23.tgz", @@ -1516,12 +1907,6 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/delegate/node_modules/dataloader": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", - "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==", - "dev": true - }, "node_modules/@graphql-tools/executor": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-0.0.20.tgz", @@ -1879,6 +2264,18 @@ "node": ">=8" } }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -2103,18 +2500,242 @@ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@repeaterjs/repeater": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.5.tgz", - "integrity": "sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA==", - "dev": true + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@repeaterjs/repeater": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", + "dev": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", + "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true }, "node_modules/@rushstack/eslint-patch": { "version": "1.10.2", @@ -2122,6 +2743,12 @@ "integrity": "sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==", "dev": true }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -2170,6 +2797,10 @@ "resolved": "packages/benchmark", "link": true }, + "node_modules/@slonik/dataloaders": { + "resolved": "packages/slonik-dataloaders", + "link": true + }, "node_modules/@slonik/driver": { "resolved": "packages/driver", "link": true @@ -2290,6 +2921,12 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2308,9 +2945,9 @@ "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==" }, "node_modules/@types/node": { - "version": "20.12.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.10.tgz", - "integrity": "sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==", + "version": "20.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", + "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2362,21 +2999,19 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", - "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", + "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/type-utils": "7.8.0", - "@typescript-eslint/utils": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", - "debug": "^4.3.4", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/type-utils": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { @@ -2538,15 +3173,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", - "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", + "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "debug": "^4.3.4" }, "engines": { @@ -2566,13 +3201,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", - "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0" + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2583,13 +3218,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", - "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", + "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/utils": "7.9.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2610,9 +3245,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", - "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2623,13 +3258,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", - "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2675,18 +3310,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", - "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", - "semver": "^7.6.0" + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2700,12 +3332,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", - "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/types": "7.9.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2722,6 +3354,111 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitest/expect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.6.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@whatwg-node/events": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.0.3.tgz", @@ -3152,6 +3889,15 @@ "node": ">=10.0.0" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/ast-types": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", @@ -3521,6 +4267,15 @@ "node": ">=10.16.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, + "engines": { + "node": ">=8" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -3614,9 +4369,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001616", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", - "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", + "version": "1.0.30001617", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz", + "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==", "dev": true, "funding": [ { @@ -3645,6 +4400,24 @@ "node": ">=12.19" } }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3720,6 +4493,18 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -4035,36 +4820,11 @@ "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" } }, - "node_modules/configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "dev": true, - "dependencies": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" - } - }, - "node_modules/configstore/node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true }, "node_modules/convert-source-map": { "version": "2.0.0", @@ -4171,55 +4931,28 @@ "node": ">= 8" } }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "dev": true, - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cspell": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-8.8.0.tgz", - "integrity": "sha512-WDysxJ1IVbPSK+bO0coGDgaA2+on9ubS+Ya2sw7opKEVpp5a0T+Fd7rEkaAmvD0ipsn8hm/S60tVNz4+m2kRUg==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-8.8.1.tgz", + "integrity": "sha512-YDJ62Q400LqxKTQV5Nk+Ub2IZGm5L0p832KbFUMy7FXDwkPLaxp3mfcLFV677FRiTcnGUtGYD+SnPkFJ2hiqsg==", "dev": true, "dependencies": { - "@cspell/cspell-json-reporter": "8.8.0", - "@cspell/cspell-pipe": "8.8.0", - "@cspell/cspell-types": "8.8.0", - "@cspell/dynamic-import": "8.8.0", + "@cspell/cspell-json-reporter": "8.8.1", + "@cspell/cspell-pipe": "8.8.1", + "@cspell/cspell-types": "8.8.1", + "@cspell/dynamic-import": "8.8.1", "chalk": "^5.3.0", "chalk-template": "^1.1.0", "commander": "^12.0.0", - "cspell-gitignore": "8.8.0", - "cspell-glob": "8.8.0", - "cspell-io": "8.8.0", - "cspell-lib": "8.8.0", + "cspell-gitignore": "8.8.1", + "cspell-glob": "8.8.1", + "cspell-io": "8.8.1", + "cspell-lib": "8.8.1", "fast-glob": "^3.3.2", "fast-json-stable-stringify": "^2.1.0", "file-entry-cache": "^8.0.0", "get-stdin": "^9.0.0", - "semver": "^7.6.0", + "semver": "^7.6.1", "strip-ansi": "^7.1.0", "vscode-uri": "^3.0.8" }, @@ -4235,12 +4968,12 @@ } }, "node_modules/cspell-config-lib": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.8.0.tgz", - "integrity": "sha512-Sz+bKXV9qpEVdi0e+MSUVMwbJ5Kin3m+6tyJp/Rk2rXKjBIXVmFBmnixv964rhr6c5GzbblV7GuMn94B2p7B3Q==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.8.1.tgz", + "integrity": "sha512-NTFfwL7Si1jp+0TINS9miXGdtPtDq95PhSbZKF9jnuBwnsnAURHCGALryLHlkRvj5JZk6dpkDfw4WxqQXdULNw==", "dev": true, "dependencies": { - "@cspell/cspell-types": "8.8.0", + "@cspell/cspell-types": "8.8.1", "comment-json": "^4.2.3", "yaml": "^2.4.2" }, @@ -4249,14 +4982,14 @@ } }, "node_modules/cspell-dictionary": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.8.0.tgz", - "integrity": "sha512-Iloe3GBJV3sVkZG0j0rmxwuNxIcuOHy+eLhFfSHKiLK62FyOBL3T2Rm50h2Q6fCiLONu/3CNp0Z3BKbQ8cZxZg==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.8.1.tgz", + "integrity": "sha512-WJqCLR/icyZc0rphEO6dZZDlSIaWIXd95QjZu3agL7a1LRLjorqhVJty6WZrV4zkOLT6PPB+qcjMxcCCxIlWiw==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "8.8.0", - "@cspell/cspell-types": "8.8.0", - "cspell-trie-lib": "8.8.0", + "@cspell/cspell-pipe": "8.8.1", + "@cspell/cspell-types": "8.8.1", + "cspell-trie-lib": "8.8.1", "fast-equals": "^5.0.1", "gensequence": "^7.0.0" }, @@ -4265,12 +4998,12 @@ } }, "node_modules/cspell-gitignore": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.8.0.tgz", - "integrity": "sha512-djM1Z0SnA1MehJHLiSRnfhY9sMPvJgQ9Gbnuub/l4VZeC0YDKIf+nAu+GPMQPtsL4QBvqj/oSy0GmUNWiMhbew==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.8.1.tgz", + "integrity": "sha512-j7wbz/VT/uZ+7oL8xwtxfA05FCv41ZrEKh1aNuMa4BI1xv/VRBmv6thOTz7aq7UcDYPNr8K5VVfBlXT/SvkEvA==", "dev": true, "dependencies": { - "cspell-glob": "8.8.0", + "cspell-glob": "8.8.1", "find-up-simple": "^1.0.0" }, "bin": { @@ -4281,9 +5014,9 @@ } }, "node_modules/cspell-glob": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.8.0.tgz", - "integrity": "sha512-LAq4PrE92vvuKoIbZN6B6HqmNlSEw9yds6pkmugYYpd6vLMBvzMbtTGjIEq0o+KhfoJPOnfOxRssPoiezwvIhA==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.8.1.tgz", + "integrity": "sha512-x2rS6gJzaF3KZB6FuNkuS3Kb3ynYns0zfC0uG/QJb9V/xc0DmAjQGQhiLIDx6XmvvDvowKmyQ/kBjY/K4VjCvQ==", "dev": true, "dependencies": { "micromatch": "^4.0.5" @@ -4293,13 +5026,13 @@ } }, "node_modules/cspell-grammar": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.8.0.tgz", - "integrity": "sha512-sB9IvSQ89Q5TgmAwXwXubs5U8dfZFvvaMT5LGKKW/XMGO4gVSBTAwRwGP2INZXdHHzFXUaVhEd+Tf4S3djoOdw==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.8.1.tgz", + "integrity": "sha512-YTyrutwIkiPH9t255l+BQtneGAkgE3uZXmnRHeIi6X+qdmnf61i8XYaSaO66VKQJX6VPZG18hBVMSUPZmYtoSw==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "8.8.0", - "@cspell/cspell-types": "8.8.0" + "@cspell/cspell-pipe": "8.8.1", + "@cspell/cspell-types": "8.8.1" }, "bin": { "cspell-grammar": "bin.mjs" @@ -4309,57 +5042,58 @@ } }, "node_modules/cspell-io": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-8.8.0.tgz", - "integrity": "sha512-98sPDQiwLTI3eNqGe6ksJv6ue5BJCMjM6CDF686Sxx2GlYe50saJK1XizM1yuhBcaHPZysSYHzbsLTMb7aSzWQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-8.8.1.tgz", + "integrity": "sha512-qwKEV2kfwT9gbq6EpYt6gcEs0oe0sDPG1YJunt8niuX4YoBe7/9ZHBfUyqNOEA+sBmvI/tjY/Wy6g9mmSRaOYA==", "dev": true, "dependencies": { - "@cspell/cspell-service-bus": "8.8.0" + "@cspell/cspell-service-bus": "8.8.1" }, "engines": { "node": ">=18" } }, "node_modules/cspell-lib": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.8.0.tgz", - "integrity": "sha512-P6WBilxNOXFWBgAFtTJn8VIBgBwn39v/TuA7d9uUWiRI/bhcMddEMCwVyjSZ3YYW/OmDEyN/c+R/4HjOw1Ny8A==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.8.1.tgz", + "integrity": "sha512-xntkgDGwDtUehM+fZudp4GbB87a2tY29G6ZqDF7WBcQsg3eyDVK/nc9KzbIfCvdWUCrsB9p+SGBoOmjYTwcocg==", "dev": true, "dependencies": { - "@cspell/cspell-bundled-dicts": "8.8.0", - "@cspell/cspell-pipe": "8.8.0", - "@cspell/cspell-resolver": "8.8.0", - "@cspell/cspell-types": "8.8.0", - "@cspell/dynamic-import": "8.8.0", - "@cspell/strong-weak-map": "8.8.0", + "@cspell/cspell-bundled-dicts": "8.8.1", + "@cspell/cspell-pipe": "8.8.1", + "@cspell/cspell-resolver": "8.8.1", + "@cspell/cspell-types": "8.8.1", + "@cspell/dynamic-import": "8.8.1", + "@cspell/strong-weak-map": "8.8.1", "clear-module": "^4.1.2", "comment-json": "^4.2.3", - "configstore": "^6.0.0", - "cspell-config-lib": "8.8.0", - "cspell-dictionary": "8.8.0", - "cspell-glob": "8.8.0", - "cspell-grammar": "8.8.0", - "cspell-io": "8.8.0", - "cspell-trie-lib": "8.8.0", + "cspell-config-lib": "8.8.1", + "cspell-dictionary": "8.8.1", + "cspell-glob": "8.8.1", + "cspell-grammar": "8.8.1", + "cspell-io": "8.8.1", + "cspell-trie-lib": "8.8.1", + "env-paths": "^3.0.0", "fast-equals": "^5.0.1", "gensequence": "^7.0.0", "import-fresh": "^3.3.0", "resolve-from": "^5.0.0", "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.0.8", + "xdg-basedir": "^5.1.0" }, "engines": { "node": ">=18" } }, "node_modules/cspell-trie-lib": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.8.0.tgz", - "integrity": "sha512-0UJp2Y5QMaBdapyIWtZ5DTH9BlM0tMcAYxOeqwmbnsdyfe2Ix8w9Ur3ZSQAAVnHEFhPJA83D0D1rMsKdYdQ4zQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.8.1.tgz", + "integrity": "sha512-S84XzQYGqKGApjVgamMp8tbHfKCeGZFxMwqedb4vxEJTYnUuCHaVPX2BK2SKaLSv/Vrd8mrJ/57sx5f8C44cFg==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "8.8.0", - "@cspell/cspell-types": "8.8.0", + "@cspell/cspell-pipe": "8.8.1", + "@cspell/cspell-types": "8.8.1", "gensequence": "^7.0.0" }, "engines": { @@ -4474,10 +5208,9 @@ } }, "node_modules/dataloader": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", - "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==", - "dev": true + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", + "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==" }, "node_modules/date-time": { "version": "3.1.0", @@ -4545,6 +5278,18 @@ "node": ">=0.10.0" } }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-freeze": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", @@ -4641,6 +5386,15 @@ "node": ">=0.3.1" } }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4664,21 +5418,6 @@ "node": ">=6.0.0" } }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", @@ -4716,9 +5455,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.757", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.757.tgz", - "integrity": "sha512-jftDaCknYSSt/+KKeXzH3LX5E2CvRLm75P3Hj+J/dv3CL0qUYcOt13d5FN1NiL5IJbbhzHrb3BomeG2tkSlZmw==", + "version": "1.4.764", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.764.tgz", + "integrity": "sha512-ZXbPV46Y4dNCA+k7YHB+BYlzcoMtZ1yH6V0tQ1ul0wmA7RiwJfS29LSdRlE1myWBXRzEgm/Lz6tryj5WVQiLmg==", "dev": true }, "node_modules/emittery": { @@ -4752,9 +5491,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -4787,6 +5526,18 @@ "node": ">=8" } }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4952,6 +5703,44 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -6276,37 +7065,93 @@ "estraverse": "^5.1.0" }, "engines": { - "node": ">=0.10" + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "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, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/execa/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { - "node": ">=4.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/execa/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, "engines": { - "node": ">=0.10.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/expect-type": { @@ -6726,6 +7571,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -6797,6 +7651,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -6814,9 +7680,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.4.tgz", - "integrity": "sha512-ofbkKj+0pjXjhejr007J/fLf+sW+8H7K5GCm+msC8q3IpvgjobpyPqSRFemNyIMxklC0zeJpi7VDFna19FacvQ==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -6951,8 +7817,6 @@ "version": "16.8.1", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "dev": true, - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -7174,6 +8038,15 @@ "resolved": "https://registry.npmjs.org/human-id/-/human-id-1.0.2.tgz", "integrity": "sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==" }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/husky": { "version": "9.0.11", "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", @@ -7635,15 +8508,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-obj-prop": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-obj-prop/-/is-obj-prop-1.0.0.tgz", @@ -8320,9 +9184,9 @@ } }, "node_modules/knip": { - "version": "5.12.3", - "resolved": "https://registry.npmjs.org/knip/-/knip-5.12.3.tgz", - "integrity": "sha512-LL+NsE+3H0TkUnQW6icHQ+5qSrPENmjHJyMHgzjiZPmunstrIsaRG+QjahnzoH/FjMjVJwrdwVOSvksa8ixFbw==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.15.1.tgz", + "integrity": "sha512-v6/Bf2qUmZ7pMJb2GjTficHyEjhP2ysIifjM1wgJr2psFbK6Vbxw2/DpeDAcgK9d99kTGh1PUQwoiFd6LHtI6A==", "dev": true, "funding": [ { @@ -8549,6 +9413,22 @@ "node": ">=4" } }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -8714,6 +9594,15 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -8882,6 +9771,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -8995,12 +9890,42 @@ "node": ">= 8.0.0" } }, + "node_modules/mlly": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz", + "integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.0", + "ufo": "^1.5.3" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -9110,6 +10035,33 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -9456,27 +10408,20 @@ } }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/onetime/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -9720,6 +10665,21 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pg": { "version": "8.11.5", "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", @@ -9844,14 +10804,6 @@ "node": ">=10" } }, - "node_modules/pg-types/node_modules/postgres-interval": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", - "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "engines": { - "node": ">=12" - } - }, "node_modules/pg/node_modules/pg-types": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", @@ -10105,6 +11057,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pkg-types": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", + "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.0", + "pathe": "^1.1.2" + } + }, "node_modules/platform": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", @@ -10142,6 +11105,34 @@ "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postgres": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", @@ -10182,9 +11173,9 @@ } }, "node_modules/postgres-interval": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-4.0.2.tgz", - "integrity": "sha512-EMsphSQ1YkQqKZL2cuG0zHkmjCCzQqQ71l2GXITqRwjhRleCdv00bDk/ktaSi0LnlaPzAc3535KTrjXsTdtx7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", "engines": { "node": ">=12" } @@ -10300,6 +11291,38 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, "node_modules/pretty-ms": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", @@ -10506,9 +11529,9 @@ } }, "node_modules/recast": { - "version": "0.23.6", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.6.tgz", - "integrity": "sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==", + "version": "0.23.7", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.7.tgz", + "integrity": "sha512-MpQlLZVpqbbxYcqEjwpRWo88sGvjOYoXptySz710RuddNMHx+wPkoNX6YyLZJlXAh5VZr1qmPrTwcTuFMh0Lag==", "dev": true, "dependencies": { "ast-types": "^0.16.1", @@ -10754,6 +11777,28 @@ "node": ">=8" } }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -10797,6 +11842,41 @@ "node": ">=18.0" } }, + "node_modules/rollup": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", + "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.17.2", + "@rollup/rollup-android-arm64": "4.17.2", + "@rollup/rollup-darwin-arm64": "4.17.2", + "@rollup/rollup-darwin-x64": "4.17.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", + "@rollup/rollup-linux-arm-musleabihf": "4.17.2", + "@rollup/rollup-linux-arm64-gnu": "4.17.2", + "@rollup/rollup-linux-arm64-musl": "4.17.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", + "@rollup/rollup-linux-riscv64-gnu": "4.17.2", + "@rollup/rollup-linux-s390x-gnu": "4.17.2", + "@rollup/rollup-linux-x64-gnu": "4.17.2", + "@rollup/rollup-linux-x64-musl": "4.17.2", + "@rollup/rollup-win32-arm64-msvc": "4.17.2", + "@rollup/rollup-win32-ia32-msvc": "4.17.2", + "@rollup/rollup-win32-x64-msvc": "4.17.2", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10877,12 +11957,9 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -10895,22 +11972,6 @@ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/serialize-error": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", @@ -11004,6 +12065,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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 + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -11231,6 +12298,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -11391,6 +12467,12 @@ "node": ">=8" } }, + "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 + }, "node_modules/stacktrace-parser": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", @@ -11410,6 +12492,12 @@ "node": ">=8" } }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, "node_modules/stream-transform": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.1.3.tgz", @@ -11583,6 +12671,18 @@ "node": ">=10" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -11606,6 +12706,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dev": true, + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true + }, "node_modules/summary": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/summary/-/summary-2.1.0.tgz", @@ -11752,6 +12870,30 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "dev": true }, + "node_modules/tinybench": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -12231,6 +13373,12 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -12260,21 +13408,6 @@ "string.fromcodepoint": "^0.2.1" } }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "dev": true, - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -12403,6 +13536,157 @@ "node": ">=12" } }, + "node_modules/vite": { + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", + "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, "node_modules/vlq": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", @@ -12585,6 +13869,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -12880,17 +14180,17 @@ } }, "node_modules/zod": { - "version": "3.23.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.6.tgz", - "integrity": "sha512-RTHJlZhsRbuA8Hmp/iNL7jnfc4nZishjsanDAfEY1QpDQZCahUp3xDzl+zfweE9BklxMUcgBgS1b7Lvie/ZVwA==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zod-validation-error": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.2.0.tgz", - "integrity": "sha512-cYlPR6zuyrgmu2wRTdumEAJGuwI7eHVHGT+VyneAQxmRAKtGRL1/7pjz4wfLhz4J05f5qoSZc3rGacswgyTjjw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.3.0.tgz", + "integrity": "sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==", "dev": true, "engines": { "node": ">=18.0.0" @@ -12900,23 +14200,21 @@ } }, "packages/benchmark": { - "name": "@slonik/benchmark", - "version": "43.0.8", + "version": "45.0.0", "dependencies": { "benny": "^3.7.1", "pg": "^8.11.3", "pg-promise": "^11.5.5", "postgres": "^3.4.4", - "slonik": "^43.0.8" + "slonik": "^45.0.0" } }, "packages/driver": { - "name": "@slonik/driver", - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { - "@slonik/types": "^43.0.8", - "@slonik/utilities": "^43.0.8", + "@slonik/types": "^45.0.0", + "@slonik/utilities": "^45.0.0", "roarr": "^7.21.1", "serialize-error": "^8.0.0", "strict-event-emitter-types": "^2.0.0" @@ -12939,20 +14237,19 @@ } }, "packages/driver/node_modules/@types/node": { - "version": "18.19.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz", - "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "packages/errors": { - "name": "@slonik/errors", - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { - "@slonik/types": "^43.0.8" + "@slonik/types": "^45.0.0" }, "devDependencies": { "@types/node": "^18.15.3", @@ -12973,24 +14270,23 @@ } }, "packages/errors/node_modules/@types/node": { - "version": "18.19.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz", - "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "packages/pg-driver": { - "name": "@slonik/pg-driver", - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { - "@slonik/driver": "^43.0.8", - "@slonik/errors": "^43.0.8", - "@slonik/sql-tag": "^43.0.8", - "@slonik/types": "^43.0.8", - "@slonik/utilities": "^43.0.8", + "@slonik/driver": "^45.0.0", + "@slonik/errors": "^45.0.0", + "@slonik/sql-tag": "^45.0.0", + "@slonik/types": "^45.0.0", + "@slonik/utilities": "^45.0.0", "pg": "^8.11.5", "pg-query-stream": "^4.5.5", "pg-types": "^4.0.2", @@ -13015,23 +14311,23 @@ } }, "packages/pg-driver/node_modules/@types/node": { - "version": "18.19.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz", - "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "packages/slonik": { - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { - "@slonik/driver": "^43.0.8", - "@slonik/errors": "^43.0.8", - "@slonik/pg-driver": "^43.0.8", - "@slonik/sql-tag": "^43.0.8", - "@slonik/utilities": "^43.0.8", + "@slonik/driver": "^45.0.0", + "@slonik/errors": "^45.0.0", + "@slonik/pg-driver": "^45.0.0", + "@slonik/sql-tag": "^45.0.0", + "@slonik/utilities": "^45.0.0", "get-stack-trace": "^3.1.1", "iso8601-duration": "^1.3.0", "postgres-interval": "^4.0.2", @@ -13060,8 +14356,181 @@ "zod": "^3" } }, + "packages/slonik-dataloaders": { + "version": "44.0.0", + "license": "BSD-3-Clause", + "dependencies": { + "dataloader": "^2.2.2", + "graphql": "^16.8.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "@slonik/types": "^44.0.0", + "eslint": "^8.57.0", + "eslint-config-canonical": "^42.8.1", + "slonik": "^44.0.0", + "typescript": "^4.5.3", + "vitest": "^1.6.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "slonik": ">=44.0.0" + } + }, + "packages/slonik-dataloaders/node_modules/@slonik/driver": { + "version": "44.0.0", + "resolved": "https://registry.npmjs.org/@slonik/driver/-/driver-44.0.0.tgz", + "integrity": "sha512-i16QQLmRezOq5+oUcpXXbNSAB7HEOfOdxHn5e5FDgXOOfP7OJt//ygP0j3hfj3lLa3wu3J5xlbT7eqsf9kqDZg==", + "dev": true, + "dependencies": { + "@slonik/types": "^44.0.0", + "@slonik/utilities": "^44.0.0", + "roarr": "^7.21.1", + "serialize-error": "^8.0.0", + "strict-event-emitter-types": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3" + } + }, + "packages/slonik-dataloaders/node_modules/@slonik/errors": { + "version": "44.0.0", + "resolved": "https://registry.npmjs.org/@slonik/errors/-/errors-44.0.0.tgz", + "integrity": "sha512-tnkzCZ/ujOXnq1K/zC6bT1PyDiAWL5rSY6yChHuQQIIZ2AgenaroxITrVui+lM86Im7RhOnqRMUd5zDQ63DmYw==", + "dev": true, + "dependencies": { + "@slonik/types": "^44.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3" + } + }, + "packages/slonik-dataloaders/node_modules/@slonik/pg-driver": { + "version": "44.0.0", + "resolved": "https://registry.npmjs.org/@slonik/pg-driver/-/pg-driver-44.0.0.tgz", + "integrity": "sha512-8fsVdGOYEhMfqUQzLtaIig847UL0Z8bX5SsDNAdcAJRd+EdSzTRn6i8FEE0Mj5ygqJu4shZtXxtF7oYnEK0vWA==", + "dev": true, + "dependencies": { + "@slonik/driver": "^44.0.0", + "@slonik/errors": "^44.0.0", + "@slonik/sql-tag": "^44.0.0", + "@slonik/types": "^44.0.0", + "@slonik/utilities": "^44.0.0", + "pg": "^8.11.5", + "pg-query-stream": "^4.5.5", + "pg-types": "^4.0.2", + "postgres-array": "^3.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3" + } + }, + "packages/slonik-dataloaders/node_modules/@slonik/sql-tag": { + "version": "44.0.0", + "resolved": "https://registry.npmjs.org/@slonik/sql-tag/-/sql-tag-44.0.0.tgz", + "integrity": "sha512-y2SuRe5xqR4ZORuqU5yAGyRC3eg6T6KmVA0HTy+kWIEzSlZQKm+lsuBswbBfY6Wl9SRGwZ4w5yzZuR/e52wzFw==", + "dev": true, + "dependencies": { + "@slonik/errors": "^44.0.0", + "@slonik/types": "^44.0.0", + "roarr": "^7.21.1", + "safe-stable-stringify": "^2.4.3", + "serialize-error": "^8.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3" + } + }, + "packages/slonik-dataloaders/node_modules/@slonik/types": { + "version": "44.0.0", + "resolved": "https://registry.npmjs.org/@slonik/types/-/types-44.0.0.tgz", + "integrity": "sha512-epVRy5QnnyBp2Rwbh+L4+VzWxtGCKN0eOU2njtp5T7h349Ci7Mwza48p8ByeSgJeIUH6BDsrEvhcpQTLvHg9lg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3" + } + }, + "packages/slonik-dataloaders/node_modules/@slonik/utilities": { + "version": "44.0.0", + "resolved": "https://registry.npmjs.org/@slonik/utilities/-/utilities-44.0.0.tgz", + "integrity": "sha512-uyvMKM0h/f8WH6X9PQEo5D/ceyM98ehYfBys6JFL2PN1UfUH/jJKBZ1QIplFHTXbsyqw+ge4B7iV2xU7CkRxEg==", + "dev": true, + "dependencies": { + "@slonik/types": "^44.0.0", + "roarr": "^7.21.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3" + } + }, + "packages/slonik-dataloaders/node_modules/postgres-interval": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-4.0.2.tgz", + "integrity": "sha512-EMsphSQ1YkQqKZL2cuG0zHkmjCCzQqQ71l2GXITqRwjhRleCdv00bDk/ktaSi0LnlaPzAc3535KTrjXsTdtx7A==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/slonik-dataloaders/node_modules/slonik": { + "version": "44.0.0", + "resolved": "https://registry.npmjs.org/slonik/-/slonik-44.0.0.tgz", + "integrity": "sha512-FLMqt7FtKDbL8ZRvOuRnY2+8sSLMWqEyRMyVuP8XP9tj78LHuS2VuFsdWflE5B6pEnmqtAQeRMYM5gD5hbkx3A==", + "dev": true, + "dependencies": { + "@slonik/driver": "^44.0.0", + "@slonik/errors": "^44.0.0", + "@slonik/pg-driver": "^44.0.0", + "@slonik/sql-tag": "^44.0.0", + "@slonik/utilities": "^44.0.0", + "get-stack-trace": "^3.1.1", + "iso8601-duration": "^1.3.0", + "postgres-interval": "^4.0.2", + "roarr": "^7.21.1", + "serialize-error": "^8.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3" + } + }, + "packages/slonik-dataloaders/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "packages/slonik-interceptor-field-name-transformation": { - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { "camelcase": "^6.2.1" @@ -13070,7 +14539,7 @@ "ava": "^5.3.1", "eslint": "^8.57.0", "eslint-config-canonical": "^42.8.1", - "slonik": "^43.0.8", + "slonik": "^45.0.0", "ts-node": "^10.4.0", "typescript": "^4.7.4" }, @@ -13078,12 +14547,13 @@ "node": ">=18" }, "peerDependencies": { - "slonik": ">=43.0.8" + "slonik": ">=45.0.0" } }, "packages/slonik-interceptor-field-name-transformation/node_modules/camelcase": { "version": "6.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "engines": { "node": ">=10" }, @@ -13093,8 +14563,9 @@ }, "packages/slonik-interceptor-field-name-transformation/node_modules/typescript": { "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13104,7 +14575,7 @@ } }, "packages/slonik-interceptor-query-cache": { - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { "roarr": "^7.21.1", @@ -13114,7 +14585,7 @@ "ava": "^5.3.1", "eslint": "^8.57.0", "eslint-config-canonical": "^42.8.1", - "slonik": "^43.0.8", + "slonik": "^45.0.0", "ts-node": "^10.4.0", "typescript": "^4.5.3" }, @@ -13122,7 +14593,7 @@ "node": ">=18" }, "peerDependencies": { - "slonik": ">=43.0.8" + "slonik": ">=45.0.0" } }, "packages/slonik-interceptor-query-cache/node_modules/typescript": { @@ -13139,7 +14610,7 @@ } }, "packages/slonik-interceptor-query-logging": { - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { "crack-json": "^1.3.0", @@ -13150,7 +14621,7 @@ "ava": "^5.3.1", "eslint": "^8.57.0", "eslint-config-canonical": "^42.8.1", - "slonik": "^43.0.8", + "slonik": "^45.0.0", "ts-node": "^10.4.0", "typescript": "^4.5.3" }, @@ -13158,19 +14629,21 @@ "node": ">=18" }, "peerDependencies": { - "slonik": ">=43.0.8" + "slonik": ">=45.0.0" } }, "packages/slonik-interceptor-query-logging/node_modules/parse-ms": { "version": "2.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", "engines": { "node": ">=6" } }, "packages/slonik-interceptor-query-logging/node_modules/pretty-ms": { "version": "7.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", "dependencies": { "parse-ms": "^2.1.0" }, @@ -13183,8 +14656,9 @@ }, "packages/slonik-interceptor-query-logging/node_modules/typescript": { "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13194,10 +14668,10 @@ } }, "packages/slonik-sql-tag-raw": { - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { - "@slonik/sql-tag": "^43.0.8", + "@slonik/sql-tag": "^45.0.0", "lodash": "^4.17.20", "roarr": "^7.21.1" }, @@ -13205,7 +14679,7 @@ "ava": "^5.3.1", "eslint": "^8.57.0", "eslint-config-canonical": "^42.8.1", - "slonik": "^43.0.8", + "slonik": "^45.0.0", "ts-node": "^10.4.0", "typescript": "^4.5.3" }, @@ -13213,7 +14687,7 @@ "node": ">=18" }, "peerDependencies": { - "slonik": ">=43.0.8" + "slonik": ">=45.0.0" } }, "packages/slonik-sql-tag-raw/node_modules/typescript": { @@ -13230,21 +14704,28 @@ } }, "packages/slonik/node_modules/@types/node": { - "version": "18.19.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz", - "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, + "packages/slonik/node_modules/postgres-interval": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-4.0.2.tgz", + "integrity": "sha512-EMsphSQ1YkQqKZL2cuG0zHkmjCCzQqQ71l2GXITqRwjhRleCdv00bDk/ktaSi0LnlaPzAc3535KTrjXsTdtx7A==", + "engines": { + "node": ">=12" + } + }, "packages/sql-tag": { - "name": "@slonik/sql-tag", - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { - "@slonik/errors": "^43.0.8", - "@slonik/types": "^43.0.8", + "@slonik/errors": "^45.0.0", + "@slonik/types": "^45.0.0", "roarr": "^7.21.1", "safe-stable-stringify": "^2.4.3", "serialize-error": "^8.0.0" @@ -13268,17 +14749,16 @@ } }, "packages/sql-tag/node_modules/@types/node": { - "version": "18.19.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz", - "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "packages/types": { - "name": "@slonik/types", - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "devDependencies": { "@types/node": "^18.15.3", @@ -13298,20 +14778,19 @@ } }, "packages/types/node_modules/@types/node": { - "version": "18.19.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz", - "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "packages/utilities": { - "name": "@slonik/utilities", - "version": "43.0.8", + "version": "45.0.0", "license": "BSD-3-Clause", "dependencies": { - "@slonik/types": "^43.0.8", + "@slonik/types": "^45.0.0", "roarr": "^7.21.1" }, "devDependencies": { @@ -13333,9 +14812,9 @@ } }, "packages/utilities/node_modules/@types/node": { - "version": "18.19.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.32.tgz", - "integrity": "sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" diff --git a/packages/slonik-dataloaders/README.md b/packages/slonik-dataloaders/README.md new file mode 100644 index 00000000..c0fe0294 --- /dev/null +++ b/packages/slonik-dataloaders/README.md @@ -0,0 +1,134 @@ +# slonik-dataloaders + +[![NPM version](http://img.shields.io/npm/v/slonik-sql-tag-raw.svg?style=flat-square)](https://www.npmjs.org/package/slonik-sql-tag-raw) +[![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) +[![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social&label=Follow)](https://twitter.com/kuizinas) + +Utilities for creating [DataLoaders](https://github.com/graphql/dataloader) using [Slonik](https://github.com/gajus/slonik). These DataLoaders abstract away some of the complexity of working with cursor-style pagination when working with a SQL database, while still maintaining the flexibility that comes with writing raw SQL statements. + +### `createNodeByIdLoaderClass` + +Example usage: + +```ts +const UserByIdLoader = createNodeByIdLoaderClass({ + query: sql.type(User)` + SELECT + * + FROM user + `, +}); + +const pool = createPool("postgresql://"); +const loader = new UserByIdLoader(pool); +const user = await loader.load(99); +``` + +By default, the loader will look for an integer column named `id` to use as the key. You can specify a different column to use like this: + +```ts +const UserByIdLoader = createNodeByIdLoaderClass({ + column: { + name: 'unique_id', + type: 'text', + } + query: sql.type(User)` + SELECT + * + FROM user + `, +}); +``` + +### `createConnectionLoaderClass` + +Example usage + +```ts +const UserConnectionLoader = createConnectionLoaderClass({ + query: sql.type(User)` + SELECT + * + FROM user + `, +}); + +const pool = createPool("postgresql://"); +const loader = new UserByIdLoader(pool); +const connection = await loader.load({ + where: ({ firstName }) => sql.fragment`${firstName} = 'Susan'`, + orderBy: ({ firstName }) => [[firstName, "ASC"]], +}); +``` + +When calling `load`, you can include `where` and `orderBy` expression factories that will be used to generate each respective clause. These factory functions allow for type-safe loader usage and abstract away the actual table alias used inside the generated SQL query. Note that the column names passed to each factory reflect the type provided when creating the loader class (i.e. `User` in the example above); however, each column name is transformed using `columnNameTransformer` as described below. + +Usage example with forward pagination: + +```ts +const connection = await loader.load({ + orderBy: ({ firstName }) => [[firstName, "ASC"]], + limit: first, + cursor: after, +}); +``` + +Usage example with backward pagination: + +```ts +const connection = await loader.load({ + orderBy: ({ firstName }) => [[firstName, "ASC"]], + limit: last, + cursor: before, + reverse: true, +}); +``` + +#### Conditionally fetching edges and count based on requested fields + +In addition to the standard `edges` and `pageInfo` fields, each connection returned by the loader also includes a `count` field. This field reflects the total number of results that _would_ be returned if no limit was applied. In order to fetch both the edges and the count, the loader makes two separate database queries. However, the loader can determine whether it needs to request only one or both of the queries by looking at the GraphQL fields that were actually requested. To do this, we pass in the `GraphQLResolveInfo` parameter provided to every GraphQL resolver: + +```ts +const connection = await loader.load({ + orderBy: ({ firstName }) => [[firstName, "ASC"]], + limit: first, + cursor: after, + info, +}); +``` + +#### Working with edge fields + +It's possible to request columns that will be exposed as fields on the edge type in your schema, as opposed to on the node type. These fields should be included in your query and the TypeScript type provided to the loader. The loader returns each row of the results as both the `edge` and the `node`, so all requested columns are available inside the resolvers for either type. Note: each requested column should be unique, so if there's a name conflict, you should use an appropriate alias. For example: + +```ts +const UserConnectionLoader = createConnectionLoaderClass< + User & { edgeCreatedAt } +>({ + query: sql.unsafe` + SELECT + user.id, + user.name, + user.created_at, + friend.created_at edge_created_at + FROM user + INNER JOIN friend ON + user.id = friend.user_id + `, +}); +``` + +In the example above, if the field on the Edge type in the schema is named `createdAt`, we just need to write a resolver for it and resolve the value to that of the `edgeCreatedAt` property. + +### `columnNameTransformer` + +Both types of loaders also accept an `columnNameTransformer` option. By default, the transformer used is [snake-case](https://www.npmjs.com/package/snake-case). The default assumes: + +- You're using conventional snake case column names; and +- You're using either [`slonik-interceptor-field-name-transformation`](https://github.com/gajus/slonik-interceptor-field-name-transformation) or the [`slonik-interceptor-preset`](https://github.com/gajus/slonik-interceptor-preset), which means the columns are returned as camelCased in the query results + +By using the `columnNameTransformer` (snake case), fields can be referenced by their names as they appear in the results when calling the loader, while still referencing the correct columns inside the query itself. If your usage doesn't meet the above two criteria, consider providing an alternative transformer, like an identify function. + +## Acknowledgments + +This library has been originally developed by @danielrearden (https://github.com/danielrearden/slonik-dataloaders), and it has been ported (with adjustments) with Daniel's permission to Slonik monorepo. \ No newline at end of file diff --git a/packages/slonik-dataloaders/package.json b/packages/slonik-dataloaders/package.json new file mode 100644 index 00000000..f4b6ea61 --- /dev/null +++ b/packages/slonik-dataloaders/package.json @@ -0,0 +1,46 @@ +{ + "author": { + "email": "gajus@gajus.com", + "name": "Gajus Kuizinas", + "url": "http://gajus.com" + }, + "dependencies": { + "dataloader": "^2.2.2", + "graphql": "^16.8.1", + "zod": "^3.23.8" + }, + "description": "Utilities for creating DataLoaders using Slonik.", + "devDependencies": { + "@slonik/types": "^44.0.0", + "eslint": "^8.57.0", + "eslint-config-canonical": "^42.8.1", + "slonik": "^44.0.0", + "typescript": "^4.5.3", + "vitest": "^1.6.0" + }, + "engines": { + "node": ">=18" + }, + "keywords": [ + "postgresql", + "interceptor", + "logging" + ], + "license": "BSD-3-Clause", + "main": "./dist/index.js", + "name": "@slonik/dataloaders", + "peerDependencies": { + "slonik": ">=44.0.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/gajus/slonik" + }, + "scripts": { + "build": "rm -fr ./dist && tsc", + "lint": "eslint ./src && tsc --noEmit", + "test": "npx vitest run" + }, + "typings": "./dist/index.d.ts", + "version": "44.0.0" +} diff --git a/packages/slonik-dataloaders/src/.eslintrc b/packages/slonik-dataloaders/src/.eslintrc new file mode 100644 index 00000000..a173a3dd --- /dev/null +++ b/packages/slonik-dataloaders/src/.eslintrc @@ -0,0 +1,26 @@ +{ + "extends": "canonical/auto", + "ignorePatterns": [ + "dist", + "package-lock.json" + ], + "overrides": [ + { + "files": "*.ts", + "rules": { + "import/no-cycle": 0, + "no-restricted-imports": ["error", "pg"] + } + }, + { + "files": ["*.test.ts", "**/*.test/*"], + "extends": "canonical/ava", + "rules": { + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-unused-expressions": 0, + "ava/no-ignored-test-files": 0 + } + } + ], + "root": true +} \ No newline at end of file diff --git a/packages/slonik-dataloaders/src/factories/createConnectionLoader.ts b/packages/slonik-dataloaders/src/factories/createConnectionLoader.ts new file mode 100644 index 00000000..dafe8e59 --- /dev/null +++ b/packages/slonik-dataloaders/src/factories/createConnectionLoader.ts @@ -0,0 +1,43 @@ +import { createConnectionLoaderClass } from './createConnectionLoaderClass'; +import { type DatabasePool, type QuerySqlToken } from 'slonik'; +import { type ZodTypeAny } from 'zod'; + +export const createConnectionLoader = ( + pool: DatabasePool, + query: QuerySqlToken, +) => { + const Loader = createConnectionLoaderClass({ + columnNameTransformer: (columnName) => { + return columnName; + }, + query, + }); + + const loader = new Loader(pool); + + type LoadParameters = Parameters[0]; + + return { + load: async (args: { + after?: string | null; + before?: string | null; + first?: number | null; + info?: LoadParameters['info']; + last?: number | null; + orderBy?: LoadParameters['orderBy']; + where?: LoadParameters['where']; + }) => { + const limit = args.first ?? args.last; + const reverse = Boolean(args.last ?? args.before); + const cursor = args.after ?? args.before; + + return await loader.load({ + cursor, + limit, + orderBy: args.orderBy, + reverse, + where: args.where, + }); + }, + }; +}; diff --git a/packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.test.ts b/packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.test.ts new file mode 100644 index 00000000..1ce50b10 --- /dev/null +++ b/packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.test.ts @@ -0,0 +1,492 @@ +import { createConnectionLoaderClass } from './createConnectionLoaderClass'; +import { type QueryResultRow } from '@slonik/types'; +import { + type FieldNode, + type GraphQLResolveInfo, + type OperationDefinitionNode, + parse, +} from 'graphql'; +import { + createPool, + type DatabasePool, + type Interceptor, + SchemaValidationError, + sql, +} from 'slonik'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import { z } from 'zod'; + +const POSTGRES_DSN = + // eslint-disable-next-line n/no-process-env + process.env.POSTGRES_DSN ?? 'postgres://postgres:postgres@localhost:5432'; + +const getInfo = ( + fields: string[], +): Pick => { + const document = parse(`{ connection { ${fields.join(' ')} } }`); + + return { + fieldNodes: [ + (document.definitions[0] as OperationDefinitionNode).selectionSet + .selections[0] as FieldNode, + ], + fragments: {}, + }; +}; + +const PersonConnectionLoader = createConnectionLoaderClass({ + query: sql.type( + z.object({ + id: z.number(), + name: z.string(), + uid: z.string(), + }), + )` + SELECT + id, + uid, + name + FROM person + `, +}); + +const getNodeIds = (edges) => edges.map(({ node }) => node.id); + +describe('createConnectionLoaderClass', () => { + let pool: DatabasePool; + + beforeAll(async () => { + pool = await createPool(POSTGRES_DSN); + + await pool.query(sql.unsafe` + CREATE TABLE IF NOT EXISTS person ( + id integer NOT NULL PRIMARY KEY, + uid text NOT NULL, + name text NOT NULL + ); + `); + + await pool.query(sql.unsafe` + INSERT INTO person + (id, uid, name) + VALUES + (1, 'a', 'aaa'), + (2, 'b', 'aaa'), + (3, 'c', 'bbb'), + (4, 'd', 'bbb'), + (5, 'e', 'ccc'), + (6, 'f', 'ccc'), + (7, 'g', 'ddd'), + (8, 'h', 'ddd'), + (9, 'i', 'eee'), + (10, 'j', 'eee'); + `); + }); + + afterAll(async () => { + if (pool) { + await pool.query(sql.unsafe` + DROP TABLE IF EXISTS person; + `); + + await pool.end(); + } + }); + + it('loads all records with no additional options', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({}); + + expect(result).toMatchObject({ + pageInfo: { + endCursor: result.edges[9].cursor, + hasNextPage: false, + hasPreviousPage: false, + startCursor: result.edges[0].cursor, + }, + }); + expect(result.edges).toHaveLength(10); + }); + + it('loads records in ascending order', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(result.edges[0].node.id).toEqual(1); + expect(result.edges[9].node.id).toEqual(10); + }); + + it('loads records in descending order', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + orderBy: ({ uid }) => [[uid, 'DESC']], + }); + + expect(result.edges[0].node.id).toEqual(10); + expect(result.edges[9].node.id).toEqual(1); + }); + + it('loads records with multiple order by expressions', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + orderBy: ({ uid, name }) => [ + [name, 'ASC'], + [uid, 'DESC'], + ], + }); + + expect(getNodeIds(result.edges)).toEqual([2, 1, 4, 3, 6, 5, 8, 7, 10, 9]); + }); + + it('loads records with complex order by expression', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + orderBy: ({ uid }) => [[sql.fragment`${uid}`, 'ASC']], + }); + + expect(getNodeIds(result.edges)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + }); + + it('loads records with where expression', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + where: ({ name }) => sql.fragment`${name} = 'eee'`, + }); + + expect(getNodeIds(result.edges)).toEqual([9, 10]); + }); + + it('loads records with limit', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(getNodeIds(result.edges)).toEqual([1, 2, 3, 4]); + }); + + it('loads records with limit and offset', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + limit: 4, + offset: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(getNodeIds(result.edges)).toEqual([5, 6, 7, 8]); + }); + + it('paginates through the records forwards', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const firstResult = await loader.load({ + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(getNodeIds(firstResult.edges)).toEqual([1, 2, 3, 4]); + expect(firstResult.pageInfo).toMatchObject({ + hasNextPage: true, + hasPreviousPage: false, + }); + + const secondResult = await loader.load({ + cursor: firstResult.pageInfo.endCursor, + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(getNodeIds(secondResult.edges)).toEqual([5, 6, 7, 8]); + expect(secondResult.pageInfo).toMatchObject({ + hasNextPage: true, + hasPreviousPage: true, + }); + + const thirdResult = await loader.load({ + cursor: secondResult.pageInfo.endCursor, + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(getNodeIds(thirdResult.edges)).toEqual([9, 10]); + expect(thirdResult.pageInfo).toMatchObject({ + hasNextPage: false, + hasPreviousPage: true, + }); + + const fourthResult = await loader.load({ + cursor: thirdResult.pageInfo.endCursor, + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(getNodeIds(fourthResult.edges)).toEqual([]); + expect(fourthResult.pageInfo).toMatchObject({ + hasNextPage: false, + hasPreviousPage: true, + }); + }); + + it('paginates through the records backwards', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const firstResult = await loader.load({ + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + reverse: true, + }); + + expect(getNodeIds(firstResult.edges)).toEqual([7, 8, 9, 10]); + expect(firstResult.pageInfo).toMatchObject({ + hasNextPage: false, + hasPreviousPage: true, + }); + + const secondResult = await loader.load({ + cursor: firstResult.pageInfo.startCursor, + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + reverse: true, + }); + + expect(getNodeIds(secondResult.edges)).toEqual([3, 4, 5, 6]); + expect(secondResult.pageInfo).toMatchObject({ + hasNextPage: true, + hasPreviousPage: true, + }); + + const thirdResult = await loader.load({ + cursor: secondResult.pageInfo.startCursor, + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + reverse: true, + }); + + expect(getNodeIds(thirdResult.edges)).toEqual([1, 2]); + expect(thirdResult.pageInfo).toMatchObject({ + hasNextPage: true, + hasPreviousPage: false, + }); + + const fourthResult = await loader.load({ + cursor: thirdResult.pageInfo.startCursor, + limit: 4, + orderBy: ({ uid }) => [[uid, 'ASC']], + reverse: true, + }); + + expect(getNodeIds(fourthResult.edges)).toEqual([]); + expect(fourthResult.pageInfo).toMatchObject({ + hasNextPage: true, + hasPreviousPage: false, + }); + }); + + it('batches loaded records', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const poolAnySpy = vi.spyOn(pool, 'any'); + + poolAnySpy.mockClear(); + + const results = await Promise.all([ + loader.load({ + orderBy: ({ uid }) => [[uid, 'ASC']], + }), + loader.load({ + orderBy: ({ uid }) => [[uid, 'DESC']], + }), + ]); + + expect(poolAnySpy).toHaveBeenCalledTimes(2); + + expect(getNodeIds(results[0].edges)).toEqual([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + ]); + expect(getNodeIds(results[1].edges)).toEqual([ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + ]); + }); + + it('caches loaded records', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const poolAnySpy = vi.spyOn(pool, 'any'); + poolAnySpy.mockClear(); + const poolOneFirstSpy = vi.spyOn(pool, 'oneFirst'); + const resultsA = await loader.load({ + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + const resultsB = await loader.load({ + orderBy: ({ uid }) => [[uid, 'ASC']], + }); + + expect(poolAnySpy).toHaveBeenCalledTimes(1); + expect(poolOneFirstSpy).toHaveBeenCalledTimes(1); + expect(getNodeIds(resultsA.edges)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect(getNodeIds(resultsB.edges)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + }); + + it('gets the count', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + info: getInfo(['count']), + where: ({ name }) => sql.fragment`${name} = 'ccc'`, + }); + + expect(result.count).toEqual(2n); + }); + + it('gets the count without fetching edges', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const result = await loader.load({ + info: getInfo(['count']), + where: ({ name }) => sql.fragment`${name} = 'ccc'`, + }); + + expect(result.count).toEqual(2n); + expect(result.edges.length).toEqual(0); + }); + + it('gets the count without fetching edges (batch)', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const results = await Promise.all([ + loader.load({ + info: getInfo(['count']), + where: ({ name }) => sql.fragment`${name} = 'ccc'`, + }), + loader.load({ + info: getInfo(['count']), + where: ({ name }) => sql.fragment`${name} = 'eee'`, + }), + ]); + + expect(results[0].count).toEqual(2n); + expect(results[0].edges.length).toEqual(0); + expect(results[1].count).toEqual(2n); + expect(results[1].edges.length).toEqual(0); + }); + + it('gets a mix of count and edges', async () => { + const loader = new PersonConnectionLoader(pool, {}); + + const results = await Promise.all([ + loader.load({ + info: getInfo(['edges']), + where: ({ name }) => sql.fragment`${name} = 'eee'`, + }), + loader.load({ + info: getInfo(['count']), + where: ({ name }) => sql.fragment`${name} = 'eee'`, + }), + ]); + + expect(results[0].count).toEqual(0); + expect(results[1].count).toEqual(2n); + }); + + it('gets the edges without fetching edges', async () => { + const loader = new PersonConnectionLoader(pool, {}); + const results = await Promise.all([ + loader.load({ + info: getInfo(['edges']), + where: ({ name }) => sql.fragment`${name} = 'ccc'`, + }), + loader.load({ + info: getInfo(['pageInfo']), + where: ({ name }) => sql.fragment`${name} = 'eee'`, + }), + ]); + + expect(results[0].count).toEqual(0); + expect(results[0].edges.length).toEqual(2); + expect(results[1].count).toEqual(0); + expect(results[1].edges.length).toEqual(2); + }); +}); + +describe('createConnectionLoaderClass (with validation)', () => { + let pool: DatabasePool; + + beforeAll(async () => { + const createResultParserInterceptor = (): Interceptor => { + return { + transformRow: (executionContext, actualQuery, row) => { + const { resultParser } = executionContext; + + // @ts-expect-error _any is not exposed through the zod typings, but does exist on ZodTypeAny + if (!resultParser || resultParser._any) { + return row; + } + + const validationResult = resultParser.safeParse(row); + + if (validationResult.success !== true) { + throw new SchemaValidationError( + actualQuery, + row, + validationResult.error.issues, + ); + } + + return validationResult.data as QueryResultRow; + }, + }; + }; + + pool = await createPool(POSTGRES_DSN, { + interceptors: [createResultParserInterceptor()], + }); + + await pool.query(sql.unsafe` + CREATE TABLE IF NOT EXISTS person ( + id integer NOT NULL PRIMARY KEY, + uid text NOT NULL, + name text NOT NULL + ); + `); + + await pool.query(sql.unsafe` + INSERT INTO person + (id, uid, name) + VALUES + (1, 'a', 'aaa'), + (2, 'b', 'aaa'), + (3, 'c', 'bbb'), + (4, 'd', 'bbb'), + (5, 'e', 'ccc'), + (6, 'f', 'ccc'), + (7, 'g', 'ddd'), + (8, 'h', 'ddd'), + (9, 'i', 'eee'), + (10, 'j', 'eee'); + `); + }); + + afterAll(async () => { + if (pool) { + await pool.query(sql.unsafe` + DROP TABLE IF EXISTS person; + `); + + await pool.end(); + } + }); + + it('fails with schema validation error', async () => { + const BadConnectionLoader = createConnectionLoaderClass({ + query: sql.type( + z.object({ + id: z.number(), + uid: z.string(), + }), + )` + SELECT + * + FROM person + `, + }); + + const loader = new BadConnectionLoader(pool, {}); + await expect(loader.load({})).rejects.toThrowError(SchemaValidationError); + }); +}); diff --git a/packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.ts b/packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.ts new file mode 100644 index 00000000..599e7545 --- /dev/null +++ b/packages/slonik-dataloaders/src/factories/createConnectionLoaderClass.ts @@ -0,0 +1,313 @@ +import { + type ColumnIdentifiers, + type Connection, + type OrderDirection, +} from '../types'; +import { fromCursor } from '../utilities/fromCursor'; +import { getColumnIdentifiers } from '../utilities/getColumnIdentifiers'; +import { getRequestedFields } from '../utilities/getRequestedFields'; +import { snakeCase } from '../utilities/snakeCase'; +import { toCursor } from '../utilities/toCursor.js'; +import DataLoader from 'dataloader'; +import { type GraphQLResolveInfo } from 'graphql'; +import { + type CommonQueryMethods, + type QuerySqlToken, + sql, + type SqlToken, +} from 'slonik'; +import { z, type ZodTypeAny } from 'zod'; + +type DataLoaderKey = { + cursor?: string | null; + info?: Pick; + limit?: number | null; + offset?: number | null; + orderBy?: ( + identifiers: ColumnIdentifiers, + ) => Array<[SqlToken, OrderDirection]>; + reverse?: boolean; + where?: (identifiers: ColumnIdentifiers) => SqlToken; +}; + +const SORT_COLUMN_ALIAS = 's1'; +const TABLE_ALIAS = 't1'; + +export const createConnectionLoaderClass = (config: { + columnNameTransformer?: (column: string) => string; + query: QuerySqlToken; +}) => { + const { columnNameTransformer = snakeCase, query } = config; + + const columnIdentifiers = getColumnIdentifiers>( + TABLE_ALIAS, + columnNameTransformer, + ); + + return class ConnectionLoaderClass extends DataLoader< + DataLoaderKey>, + Connection>, + string + > { + public constructor( + pool: CommonQueryMethods, + dataLoaderOptions?: DataLoader.Options< + DataLoaderKey>, + Connection>, + string + >, + ) { + super( + async (loaderKeys) => { + const edgesQueries: Array = []; + const countQueries: Array = []; + + for (const loaderKey of loaderKeys.values()) { + const { + cursor, + info, + limit, + offset, + orderBy, + reverse = false, + where, + } = loaderKey; + + // If a GraphQLResolveInfo object was not provided, we will assume both pageInfo and edges were requested + const requestedFields = info + ? getRequestedFields(info) + : new Set(['pageInfo', 'edges', 'count']); + + const conditions: SqlToken[] = where + ? [sql.fragment`(${where(columnIdentifiers)})`] + : []; + + if (requestedFields.has('count')) { + countQueries.push( + sql.unsafe`( + SELECT count(*) count + FROM ( + ${query} + ) ${sql.identifier([TABLE_ALIAS])} + WHERE ${ + conditions.length + ? sql.fragment`${sql.join( + conditions, + sql.fragment` AND `, + )}` + : sql.fragment`TRUE` + } + )`, + ); + } else { + countQueries.push(null); + } + + if ( + requestedFields.has('pageInfo') || + requestedFields.has('edges') + ) { + const orderByExpressions: Array<[SqlToken, OrderDirection]> = + orderBy ? orderBy(columnIdentifiers) : []; + + const selectExpressions = [ + sql.fragment`${sql.identifier([TABLE_ALIAS])}.*`, + sql.fragment`json_build_array(${ + orderByExpressions.length + ? sql.join( + orderByExpressions.map(([expression]) => expression), + sql.fragment`,`, + ) + : sql.fragment`` + }) ${sql.identifier([SORT_COLUMN_ALIAS])}`, + ]; + + const orderByClause = orderByExpressions.length + ? sql.fragment`ORDER BY ${sql.join( + orderByExpressions.map( + ([expression, direction]) => + sql.fragment`${expression} ${ + direction === (reverse ? 'DESC' : 'ASC') + ? sql.fragment`ASC` + : sql.fragment`DESC` + }`, + ), + sql.fragment`,`, + )}` + : sql.fragment``; + + if (cursor) { + const values = fromCursor(cursor); + conditions.push( + sql.fragment`(${sql.join( + orderByExpressions.map((_orderByExpression, outerIndex) => { + const expressions = orderByExpressions.slice( + 0, + outerIndex + 1, + ); + return sql.fragment`(${sql.join( + expressions.map( + ([expression, direction], innerIndex) => { + let comparisonOperator = sql.fragment`=`; + if (innerIndex === expressions.length - 1) { + comparisonOperator = + direction === (reverse ? 'DESC' : 'ASC') + ? sql.fragment`>` + : sql.fragment`<`; + } + + return sql.fragment`${expression} ${comparisonOperator} ${values[innerIndex]}`; + }, + ), + sql.fragment` AND `, + )})`; + }), + sql.fragment` OR `, + )})`, + ); + } + + const whereExpression = conditions.length + ? sql.fragment`${sql.join(conditions, sql.fragment` AND `)}` + : sql.fragment`TRUE`; + + edgesQueries.push( + sql.unsafe`( + SELECT + ${sql.join(selectExpressions, sql.fragment`, `)} + FROM ( + ${query} + ) ${sql.identifier([TABLE_ALIAS])} + WHERE ${whereExpression} + ${orderByClause} + LIMIT ${limit ? limit + 1 : null} + OFFSET ${offset || 0} + )`, + ); + } else { + edgesQueries.push(null); + } + } + + let edgeSchema: ZodTypeAny = z.any(); + + if ('shape' in query.parser) { + edgeSchema = z + .object({ + [SORT_COLUMN_ALIAS]: z.array(z.any()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(query.parser as any).shape, + }) + .strict(); + } + + const countSchema = z.object({ + count: z.number(), + }); + + const [edgeResults, countResults] = await Promise.all([ + Promise.all( + edgesQueries.map((edgesQuery) => { + return edgesQuery === null + ? [] + : pool.any(sql.type(edgeSchema)`${edgesQuery}`); + }), + ), + Promise.all( + countQueries.map((countQuery) => { + return countQuery === null + ? 0 + : pool.oneFirst(sql.type(countSchema)`${countQuery}`); + }), + ), + ]); + + const connections = loaderKeys.map((loaderKey, loaderKeyIndex) => { + const { cursor, limit, reverse = false } = loaderKey; + + const edges = (edgeResults[loaderKeyIndex] ?? []).map((record) => { + const cursorValues: string[] = []; + + let index = 0; + + while (true) { + const value = record[SORT_COLUMN_ALIAS]?.[index]; + if (value === undefined) { + break; + } else { + cursorValues.push(value); + index++; + } + } + + // TODO add a test for this + // Strip out `__typename`, otherwise if the connection object is returned inside a resolver, + // GraphQL will throw an error because the typename won't match the edge type + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/naming-convention + const { __typename, ...edgeFields } = record; + + return { + ...edgeFields, + cursor: toCursor(cursorValues), + node: record, + }; + }); + + const slicedEdges = edges.slice( + 0, + limit === null ? undefined : limit, + ); + + if (reverse) { + slicedEdges.reverse(); + } + + const hasMore = Boolean(edges.length > slicedEdges.length); + const pageInfo = { + endCursor: slicedEdges[slicedEdges.length - 1]?.cursor || null, + hasNextPage: reverse ? Boolean(cursor) : hasMore, + hasPreviousPage: reverse ? hasMore : Boolean(cursor), + startCursor: slicedEdges[0]?.cursor || null, + }; + + const count = countResults[loaderKeyIndex] ?? 0; + + return { + count, + edges: slicedEdges, + pageInfo, + }; + }); + + return connections; + }, + { + ...dataLoaderOptions, + cacheKeyFn: ({ + cursor, + info, + limit, + offset, + orderBy, + reverse = false, + where, + }) => { + const requestedFields = info + ? getRequestedFields(info) + : new Set(['pageInfo', 'edges']); + + return JSON.stringify({ + cursor, + limit, + offset, + orderBy: orderBy?.(columnIdentifiers), + requestedFields: Array.from(requestedFields), + reverse, + where: where?.(columnIdentifiers), + }); + }, + }, + ); + } + }; +}; diff --git a/packages/slonik-dataloaders/src/factories/createListLoader.ts b/packages/slonik-dataloaders/src/factories/createListLoader.ts new file mode 100644 index 00000000..657b5d06 --- /dev/null +++ b/packages/slonik-dataloaders/src/factories/createListLoader.ts @@ -0,0 +1,32 @@ +import { createConnectionLoader } from './createConnectionLoader'; +import { type DatabasePool, type QuerySqlToken } from 'slonik'; +import { type ZodTypeAny } from 'zod'; + +export const createListLoader = ( + pool: DatabasePool, + query: QuerySqlToken, +) => { + const loader = createConnectionLoader(pool, query); + + type LoadParameters = Parameters[0]; + + return { + load: async (args: { + orderBy?: LoadParameters['orderBy']; + where?: LoadParameters['where']; + }) => { + const nodes: T[] = []; + + const connection = await loader.load({ + orderBy: args.orderBy, + where: args.where, + }); + + for (const edge of connection.edges) { + nodes.push(edge.node); + } + + return nodes; + }, + }; +}; diff --git a/packages/slonik-dataloaders/src/factories/createNodeByIdLoader.ts b/packages/slonik-dataloaders/src/factories/createNodeByIdLoader.ts new file mode 100644 index 00000000..ea8b5149 --- /dev/null +++ b/packages/slonik-dataloaders/src/factories/createNodeByIdLoader.ts @@ -0,0 +1,14 @@ +import { createNodeByIdLoaderClass } from './createNodeByIdLoaderClass'; +import { type DatabasePool, type QuerySqlToken } from 'slonik'; +import { type ZodTypeAny } from 'zod'; + +export const createNodeByIdLoader = ( + pool: DatabasePool, + query: QuerySqlToken, +) => { + const Loader = createNodeByIdLoaderClass({ + query, + }); + + return new Loader(pool); +}; diff --git a/packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.test.ts b/packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.test.ts new file mode 100644 index 00000000..f0d9c846 --- /dev/null +++ b/packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.test.ts @@ -0,0 +1,104 @@ +import { createNodeByIdLoaderClass } from './createNodeByIdLoaderClass.js'; +import { createPool, type DatabasePool, sql } from 'slonik'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import { z } from 'zod'; + +const POSTGRES_DSN = + // eslint-disable-next-line n/no-process-env + process.env.POSTGRES_DSN ?? 'postgres://postgres:postgres@localhost:5432'; + +const FooByIdLoader = createNodeByIdLoaderClass({ + query: sql.type( + z.object({ + id: z.number(), + uid: z.string(), + }), + )` + SELECT + * + FROM test_table_foo + `, +}); + +describe('createRecordByUniqueColumnLoader', () => { + let pool: DatabasePool; + + beforeAll(async () => { + pool = await createPool(POSTGRES_DSN); + + await pool.query(sql.unsafe` + CREATE TABLE IF NOT EXISTS test_table_foo ( + id integer NOT NULL PRIMARY KEY, + uid text NOT NULL + ); + `); + + await pool.query(sql.unsafe` + INSERT INTO test_table_foo + (id, uid) + VALUES + (1, 'a'), + (2, 'b'), + (3, 'c'); + `); + }); + + afterAll(async () => { + if (pool) { + await pool.query(sql.unsafe` + DROP TABLE IF EXISTS test_table_foo; + `); + + await pool.end(); + } + }); + + it('loads record by numeric column', async () => { + const loader = new FooByIdLoader(pool, {}); + const result = await loader.load(2); + + expect(result).toMatchObject({ id: 2, uid: 'b' }); + }); + + it("returns null when a match can't be found", async () => { + const loader = new FooByIdLoader(pool, {}); + const result = await loader.load(999); + + expect(result).toEqual(null); + }); + + it('batches and caches loaded records', async () => { + const loader = new FooByIdLoader(pool, {}); + const poolAnySpy = vi.spyOn(pool, 'any'); + const results = await Promise.all([loader.load(3), loader.load(2)]); + + expect(poolAnySpy).toHaveBeenCalledTimes(1); + expect(results).toMatchObject([ + { id: 3, uid: 'c' }, + { id: 2, uid: 'b' }, + ]); + }); + + it('loads record by text column', async () => { + const FooByUidLoader = createNodeByIdLoaderClass({ + column: { + name: 'uid', + type: 'text', + }, + query: sql.type( + z.object({ + id: z.number(), + uid: z.string(), + }), + )` + SELECT + * + FROM test_table_foo + `, + }); + const loader = new FooByUidLoader(pool, {}); + const result = await loader.load('b'); + + expect(result).toMatchObject({ id: 2, uid: 'b' }); + }); +}); diff --git a/packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.ts b/packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.ts new file mode 100644 index 00000000..f4bafc86 --- /dev/null +++ b/packages/slonik-dataloaders/src/factories/createNodeByIdLoaderClass.ts @@ -0,0 +1,80 @@ +import { snakeCase } from '../utilities/snakeCase'; +import DataLoader from 'dataloader'; +import { + type CommonQueryMethods, + type FragmentSqlToken, + type PrimitiveValueExpression, + type QuerySqlToken, + sql, + type TypeNameIdentifier, +} from 'slonik'; +import { type z, type ZodTypeAny } from 'zod'; + +const TABLE_ALIAS = 't1'; + +export const createNodeByIdLoaderClass = (config: { + column?: { + name?: Extract, string> | undefined; + type?: FragmentSqlToken | TypeNameIdentifier; + }; + columnNameTransformer?: ((column: string) => string) | undefined; + query: QuerySqlToken; +}) => { + const { + column: { name: columnName = 'id', type: columnType = 'int4' } = {}, + columnNameTransformer = snakeCase, + query, + } = config; + + return class NodeLoader extends DataLoader< + PrimitiveValueExpression, + z.infer, + string + > { + public constructor( + pool: CommonQueryMethods, + loaderOptions?: DataLoader.Options< + PrimitiveValueExpression, + z.infer, + string + >, + ) { + super( + async (loaderKeys) => { + const where = sql.fragment`${sql.identifier([ + TABLE_ALIAS, + columnNameTransformer(columnName), + ])} = ANY(${sql.array(loaderKeys, columnType)})`; + + const records = await pool.any( + sql.type(query.parser)` + SELECT * + FROM ( + ${query} + ) ${sql.identifier([TABLE_ALIAS])} + WHERE ${where} + `, + ); + + const recordsByLoaderKey = loaderKeys.map((value) => { + const targetRecord = records.find((record) => { + return String(record[columnName]) === String(value); + }); + + if (targetRecord) { + return targetRecord; + } + + return null; + }); + + return recordsByLoaderKey; + }, + { + ...loaderOptions, + cacheKeyFn: String, + }, + ); + } + }; +}; diff --git a/packages/slonik-dataloaders/src/index.ts b/packages/slonik-dataloaders/src/index.ts new file mode 100644 index 00000000..9d5dfe20 --- /dev/null +++ b/packages/slonik-dataloaders/src/index.ts @@ -0,0 +1,5 @@ +export { createConnectionLoader } from './factories/createConnectionLoader'; +export { createConnectionLoaderClass } from './factories/createConnectionLoaderClass'; +export { createListLoader } from './factories/createListLoader'; +export { createNodeByIdLoader } from './factories/createNodeByIdLoader'; +export { createNodeByIdLoaderClass } from './factories/createNodeByIdLoaderClass'; diff --git a/packages/slonik-dataloaders/src/types.ts b/packages/slonik-dataloaders/src/types.ts new file mode 100644 index 00000000..2690b469 --- /dev/null +++ b/packages/slonik-dataloaders/src/types.ts @@ -0,0 +1,21 @@ +import { type IdentifierSqlToken } from 'slonik'; + +export type OrderDirection = 'ASC' | 'DESC'; + +export type ColumnIdentifiers = Record< + keyof TResult, + IdentifierSqlToken +>; + +type PageInfo = { + endCursor: string | null; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; +}; + +export type Connection = { + count: number; + edges: Array; + pageInfo: PageInfo; +}; diff --git a/packages/slonik-dataloaders/src/utilities/fromCursor.ts b/packages/slonik-dataloaders/src/utilities/fromCursor.ts new file mode 100644 index 00000000..2d8d1c89 --- /dev/null +++ b/packages/slonik-dataloaders/src/utilities/fromCursor.ts @@ -0,0 +1,5 @@ +import { type QueryResultRowColumn } from 'slonik'; + +export const fromCursor = (cursor: string): QueryResultRowColumn[] => { + return JSON.parse(Buffer.from(cursor, 'base64').toString()); +}; diff --git a/packages/slonik-dataloaders/src/utilities/getColumnIdentifiers.ts b/packages/slonik-dataloaders/src/utilities/getColumnIdentifiers.ts new file mode 100644 index 00000000..a83b1f4c --- /dev/null +++ b/packages/slonik-dataloaders/src/utilities/getColumnIdentifiers.ts @@ -0,0 +1,15 @@ +import { snakeCase } from './snakeCase'; +import { type IdentifierSqlToken, sql } from 'slonik'; + +export const getColumnIdentifiers = ( + tableAlias: string, + columnNameTransformer: (column: string) => string = snakeCase, +) => { + return new Proxy( + {}, + { + get: (_target, property: string) => + sql.identifier([tableAlias, columnNameTransformer(property)]), + }, + ) as Record; +}; diff --git a/packages/slonik-dataloaders/src/utilities/getRequestedFields.ts b/packages/slonik-dataloaders/src/utilities/getRequestedFields.ts new file mode 100644 index 00000000..03fa6046 --- /dev/null +++ b/packages/slonik-dataloaders/src/utilities/getRequestedFields.ts @@ -0,0 +1,47 @@ +import { + type FragmentDefinitionNode, + type GraphQLResolveInfo, + type SelectionSetNode, +} from 'graphql'; + +const addFieldNamesFromSelectionSet = ( + fieldNames: Set, + selectionSet: SelectionSetNode, + fragments: { [key: string]: FragmentDefinitionNode }, +): void => { + for (const selection of selectionSet.selections) { + if (selection.kind === 'FragmentSpread') { + addFieldNamesFromSelectionSet( + fieldNames, + fragments[selection.name.value].selectionSet, + fragments, + ); + } else if (selection.kind === 'InlineFragment') { + addFieldNamesFromSelectionSet( + fieldNames, + selection.selectionSet, + fragments, + ); + } else { + fieldNames.add(selection.name.value); + } + } +}; + +export const getRequestedFields = ( + info: Pick, +): Set => { + const fieldNames = new Set(); + + for (const fieldNode of info.fieldNodes) { + if (fieldNode.selectionSet) { + addFieldNamesFromSelectionSet( + fieldNames, + fieldNode.selectionSet, + info.fragments, + ); + } + } + + return fieldNames; +}; diff --git a/packages/slonik-dataloaders/src/utilities/snakeCase.ts b/packages/slonik-dataloaders/src/utilities/snakeCase.ts new file mode 100644 index 00000000..08f39355 --- /dev/null +++ b/packages/slonik-dataloaders/src/utilities/snakeCase.ts @@ -0,0 +1,36 @@ +/** + * Adopted from https://npmjs.com/snake-case + */ + +// Regexps involved with splitting words in various case formats. +const SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu; +const SPLIT_UPPER_UPPER_RE = /(\p{Lu})(\p{Lu}\p{Ll})/gu; +// Regexp involved with stripping non-word characters from the result. +const DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu; +// The replacement value for splits. +const SPLIT_REPLACE_VALUE = '$1\0$2'; + +/** + * Split any cased input strings into an array of words. + */ +const split = (input: string): string[] => { + let result = input + .replaceAll(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE) + .replaceAll(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE); + + result = result.replaceAll(DEFAULT_STRIP_REGEXP, '\0'); + let start = 0; + let end = result.length; + // Trim the delimiter from around the output string. + while (result.charAt(start) === '\0') start++; + if (start === end) return []; + while (result.charAt(end - 1) === '\0') end--; + // Transform each token independently. + return result.slice(start, end).split(/\0/gu); +}; + +export const snakeCase = (input: string) => { + return split(input) + .map((fragment) => fragment.toLowerCase()) + .join('_'); +}; diff --git a/packages/slonik-dataloaders/src/utilities/toCursor.ts b/packages/slonik-dataloaders/src/utilities/toCursor.ts new file mode 100644 index 00000000..9de65914 --- /dev/null +++ b/packages/slonik-dataloaders/src/utilities/toCursor.ts @@ -0,0 +1,5 @@ +import { type QueryResultRowColumn } from 'slonik'; + +export const toCursor = (ids: QueryResultRowColumn[]): string => { + return Buffer.from(JSON.stringify(ids)).toString('base64'); +}; diff --git a/packages/slonik-dataloaders/tsconfig.json b/packages/slonik-dataloaders/tsconfig.json new file mode 100644 index 00000000..cefba6aa --- /dev/null +++ b/packages/slonik-dataloaders/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "lib": [ + "es2021" + ], + "module": "commonjs", + "moduleResolution": "node", + "noImplicitAny": false, + "noImplicitReturns": true, + "outDir": "dist", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "es2020", + "useUnknownInCatchVariables": false + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/packages/slonik-sql-tag-raw/src/factories/helpers.test/createTestRunner.ts b/packages/slonik-sql-tag-raw/src/factories/helpers.test/createTestRunner.ts index a2eadf39..1f58a16a 100644 --- a/packages/slonik-sql-tag-raw/src/factories/helpers.test/createTestRunner.ts +++ b/packages/slonik-sql-tag-raw/src/factories/helpers.test/createTestRunner.ts @@ -3,8 +3,9 @@ import { createPool, sql } from 'slonik'; // TODO deduplicate with slonik/src/factories/createTestRunner.ts -// eslint-disable-next-line n/no-process-env -const POSTGRES_DSN = process.env.POSTGRES_DSN ?? 'postgres@localhost:5432'; +const POSTGRES_DSN = + // eslint-disable-next-line n/no-process-env + process.env.POSTGRES_DSN ?? 'postgresql://postgres:postgres@localhost:5432'; type TestContextType = { dsn: string; @@ -25,11 +26,11 @@ export const createTestRunner = () => { const TEST_DATABASE_NAME = ['slonik_test', String(testId)].join('_'); t.context = { - dsn: 'postgresql://' + POSTGRES_DSN + '/' + TEST_DATABASE_NAME, + dsn: POSTGRES_DSN + '/' + TEST_DATABASE_NAME, testDatabaseName: TEST_DATABASE_NAME, }; - const pool0 = await createPool('postgresql://' + POSTGRES_DSN, { + const pool0 = await createPool(POSTGRES_DSN, { maximumPoolSize: 1, }); diff --git a/packages/slonik/src/helpers.test/createTestRunner.ts b/packages/slonik/src/helpers.test/createTestRunner.ts index da77596a..28100333 100644 --- a/packages/slonik/src/helpers.test/createTestRunner.ts +++ b/packages/slonik/src/helpers.test/createTestRunner.ts @@ -3,8 +3,9 @@ import { createPool } from '../factories/createPool'; import { type DriverFactory } from '@slonik/driver'; import anyTest, { type TestFn } from 'ava'; -// eslint-disable-next-line n/no-process-env -const POSTGRES_DSN = process.env.POSTGRES_DSN ?? 'postgres@localhost:5432'; +const POSTGRES_DSN = + // eslint-disable-next-line n/no-process-env + process.env.POSTGRES_DSN ?? 'postgresql://postgres:postgres@localhost:5432'; export type TestContextType = { dsn: string; @@ -28,11 +29,11 @@ export const createTestRunner = ( const TEST_DATABASE_NAME = ['slonik_test', name, String(testId)].join('_'); t.context = { - dsn: 'postgresql://' + POSTGRES_DSN + '/' + TEST_DATABASE_NAME, + dsn: POSTGRES_DSN + '/' + TEST_DATABASE_NAME, testDatabaseName: TEST_DATABASE_NAME, }; - const pool0 = await createPool('postgresql://' + POSTGRES_DSN, { + const pool0 = await createPool(POSTGRES_DSN, { driverFactory, maximumPoolSize: 1, });