diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f27f863b..8ce78c0b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @supabase/api @supabase/frontend +* @avallete @soedirgo @supabase/sdk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5afcd6ee..71bc8f6c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,36 +6,36 @@ on: - master workflow_dispatch: -permissions: - contents: read - jobs: semantic-release: name: Release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: new-release-published: ${{ steps.semantic-release.outputs.new_release_published }} new-release-version: ${{ steps.semantic-release.outputs.new_release_version }} permissions: contents: write + id-token: write steps: - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' + - name: Update npm + run: npm install -g npm@latest + - run: | npm clean-install npm run build - id: semantic-release - uses: cycjimmy/semantic-release-action@v4 + uses: cycjimmy/semantic-release-action@v6 with: - semantic_version: 21 + semantic_version: 25.0.1 # version with latest npm and support for trusted publishing env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} docker-hub: name: Release on Docker Hub diff --git a/.github/workflows/validate-python-types.yml b/.github/workflows/validate-python-types.yml new file mode 100644 index 00000000..1e5809b1 --- /dev/null +++ b/.github/workflows/validate-python-types.yml @@ -0,0 +1,71 @@ +name: Validate Python Type Generation + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + validate-python-types: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build project + run: npm run build + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install Python dependencies + run: | + pip install pydantic mypy + + - name: Start test database + working-directory: test/db + run: | + docker compose up -d --wait + + - name: Wait for database to be ready + run: | + # Install PostgreSQL client for health check + sudo apt-get update && sudo apt-get install -y postgresql-client + until pg_isready -h localhost -p 5432 -U postgres; do + echo "Waiting for database..." + sleep 1 + done + echo "Database is ready!" + + - name: Generate Python types + id: generate-types + run: | + node --loader ts-node/esm scripts/generate-python-types-test.ts > generated_types.py + echo "Generated Python types (first 30 lines):" + head -30 generated_types.py + + - name: Validate Python types runtime + run: | + python -c "import generated_types; print('✓ Generated Python types are valid and can be imported')" + + - name: Validate Python types with mypy + run: | + mypy generated_types.py --strict + + - name: Cleanup + if: always() + working-directory: test/db + run: docker compose down diff --git a/package-lock.json b/package-lock.json index 2787f0d6..e8fe798a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "pg-connection-string": "^2.7.0", "pg-format": "^1.0.4", "pg-protocol": "npm:@supabase/pg-protocol@0.0.2", - "pgsql-parser": "^13.16.0", + "pgsql-parser": "^17.8.2", "pino": "^9.5.0", "postgres-array": "^3.0.1", "prettier": "^3.3.3", @@ -639,11 +639,28 @@ "@sinclair/typebox": ">=0.26 <=0.32" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "license": "MIT" + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -780,66 +797,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -878,51 +835,12 @@ "node": ">= 8" } }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -944,6 +862,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" }, @@ -980,6 +899,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", @@ -1519,6 +1439,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.32.0.tgz", "integrity": "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -1539,9 +1460,9 @@ } }, "node_modules/@pgsql/types": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/@pgsql/types/-/types-13.9.0.tgz", - "integrity": "sha512-R26mn0zMkwfR8imEQ1Q4NedHwG9gTUfgVnLJUBqPn33JyhOUi2H6iEVTcC9kHAm7gQGpwSBKfuCItWgenAlm9g==", + "version": "17.6.1", + "resolved": "https://registry.npmjs.org/@pgsql/types/-/types-17.6.1.tgz", + "integrity": "sha512-Hk51+nyOxS7Dy5oySWywyNZxo5HndX1VDXT4ZEBD+p+vvMFM2Vc+sKSuByCiI8banou4edbgdnOC251IOuq7QQ==", "license": "SEE LICENSE IN LICENSE" }, "node_modules/@pkgjs/parseargs": { @@ -1956,16 +1877,8 @@ "version": "0.31.28", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.28.tgz", "integrity": "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==", - "license": "MIT" - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "license": "MIT", - "engines": { - "node": ">= 6" - } + "peer": true }, "node_modules/@tsconfig/node10": { "version": "1.0.11", @@ -2032,6 +1945,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.28.tgz", "integrity": "sha512-DHlH/fNL6Mho38jTy7/JT7sn2wnXI+wULR6PV4gy4VHLVvnrV/d3pHAMQHhc4gjdLmK2ZiPoMxzp6B3yRajLSQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -2224,12 +2138,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC" - }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", @@ -2241,6 +2149,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2270,43 +2179,6 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -2360,6 +2232,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2392,26 +2265,6 @@ "node": ">= 8" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2536,6 +2389,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/big-integer": { @@ -2570,6 +2424,7 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -2599,66 +2454,6 @@ "node": ">=8" } }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -2799,30 +2594,12 @@ "fsevents": "~2.3.2" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/cjs-module-lexer": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "license": "MIT" }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/close-with-grace": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/close-with-grace/-/close-with-grace-2.2.0.tgz", @@ -2846,15 +2623,6 @@ "dev": true, "license": "MIT" }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -2872,14 +2640,9 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, "license": "MIT" }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC" - }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", @@ -3117,12 +2880,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT" - }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -3161,12 +2918,6 @@ "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", "license": "MIT" }, - "node_modules/dotty": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dotty/-/dotty-0.1.2.tgz", - "integrity": "sha512-V0EWmKeH3DEhMwAZ+8ZB2Ao4OK6p++Z0hsDtZq3N0+0ZMVqkzrcEGROvOnZpLnvBg5PTNG23JEDLAm64gPaotQ==", - "license": "BSD-3-Clause" - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3193,18 +2944,9 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -3215,21 +2957,6 @@ "once": "^1.4.0" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "license": "MIT" - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3557,9 +3284,9 @@ "license": "MIT" }, "node_modules/fastify": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.29.0.tgz", - "integrity": "sha512-MaaUHUGcCgC8fXQDsDtioaCcag1fmPJ9j64vAKunqZF4aSub040ZGi/ag8NGE2714yREPOKZuHCfpPzuUD3UQQ==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.29.1.tgz", + "integrity": "sha512-m2kMNHIG92tSNWv+Z3UeTR9AWLLuo7KctC7mlFPtMEVrfjIhmQhkQnT9v15qA/BfVq3vvj134Y0jl9SBje3jXQ==", "funding": [ { "type": "github", @@ -3571,6 +3298,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@fastify/ajv-compiler": "^3.5.0", "@fastify/error": "^3.4.0", @@ -3770,24 +3498,6 @@ "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", "license": "MIT" }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3843,27 +3553,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -3934,21 +3623,24 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3967,6 +3659,22 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -4021,6 +3729,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/has-bigints": { @@ -4104,12 +3813,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC" - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4143,69 +3846,14 @@ "dev": true, "license": "MIT" }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" + "node": ">= 4" } }, "node_modules/ignore-by-default": { @@ -4227,47 +3875,6 @@ "module-details-from-path": "^1.0.3" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "license": "ISC" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4283,19 +3890,6 @@ "node": ">= 0.4" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -4489,6 +4083,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4526,12 +4121,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "license": "MIT" - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -4728,6 +4317,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -4785,9 +4375,9 @@ } }, "node_modules/jackspeak": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", - "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -4810,12 +4400,6 @@ "node": ">=10" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT" - }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -4878,15 +4462,12 @@ } }, "node_modules/libpg-query": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/libpg-query/-/libpg-query-13.3.2.tgz", - "integrity": "sha512-6ft2qyk+LO1hdmPU389RvN7inRGLU0T8Ge4RG+q4usE+dAA4nl+WVp4HVpBC+1Ku4lgxM38PkoW7OzAw8VDebA==", - "hasInstallScript": true, + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/libpg-query/-/libpg-query-17.6.0.tgz", + "integrity": "sha512-r4zOTcLTGYS5PlLQAicJ6Yi/tvZFag42YUuNEO8pi8bwt/ZZ4kj514J4QV5bOx0mZzPLF6agbfNXQVxGgmHR8g==", "license": "LICENSE IN LICENSE", "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.8", - "node-addon-api": "^1.6.3", - "node-gyp": "^8.0.0" + "@pgsql/types": "^17.6.0" } }, "node_modules/light-my-request": { @@ -4923,18 +4504,6 @@ "dev": true, "license": "MIT" }, - "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==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -4980,33 +4549,6 @@ "dev": true, "license": "ISC" }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -5067,6 +4609,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -5079,111 +4622,20 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "license": "MIT", - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" } }, "node_modules/mnemonist": { @@ -5254,15 +4706,6 @@ "url": "https://nearley.js.org/#give-to-nearley" } }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/nested-error-stacks": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", @@ -5289,137 +4732,6 @@ "node": ">=10" } }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp/node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-gyp/node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-gyp/node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/node-sql-parser": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-4.18.0.tgz", @@ -5484,21 +4796,6 @@ "node": ">=4" } }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -5558,28 +4855,6 @@ "node": ">= 4" } }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -5649,6 +4924,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -5832,15 +5108,6 @@ "node": ">=4" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -5884,16 +5151,6 @@ "node": "20 || >=22" } }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6019,7 +5276,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.7.0", "pg-pool": "^3.8.0", @@ -6071,8 +5327,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/pg/node_modules/postgres-array": { "version": "2.0.0", @@ -6123,35 +5378,23 @@ } }, "node_modules/pgsql-deparser": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/pgsql-deparser/-/pgsql-deparser-13.15.0.tgz", - "integrity": "sha512-6d4YeDE/y+AZ/C4tlzTrFwbOqDW4ma/jvYlXRgXYVdPU2WF5IQISksIQ8uhNMXW7QxL/4gw0bzLhRNwckf3t/Q==", + "version": "17.11.1", + "resolved": "https://registry.npmjs.org/pgsql-deparser/-/pgsql-deparser-17.11.1.tgz", + "integrity": "sha512-BGKgwC4qs+FPcG8Ai989LO6i4E8KF5HEvlTnI8uhS4qUyu6P1xCyP9pJDky95ZL8DolaGUDFAJtxteDBw33OCg==", "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@pgsql/types": "^13.9.0", - "dotty": "^0.1.0", - "pgsql-enums": "^13.10.0" + "@pgsql/types": "^17.6.1" } }, - "node_modules/pgsql-enums": { - "version": "13.10.0", - "resolved": "https://registry.npmjs.org/pgsql-enums/-/pgsql-enums-13.10.0.tgz", - "integrity": "sha512-L0vO9RwwPENvB07YlIVTnRu3JMnmjHQhxWR2NQbHOUPIpfF6khhfv+OC51By2ATts3jfZRSi8TLjNf9O6rP9iA==", - "license": "SEE LICENSE IN LICENSE" - }, "node_modules/pgsql-parser": { - "version": "13.16.0", - "resolved": "https://registry.npmjs.org/pgsql-parser/-/pgsql-parser-13.16.0.tgz", - "integrity": "sha512-LdHFWjotgN7y2rEAb2K/LeLZrMJvpLy0Qe+1+8ZByf5C2pmKTo98VXiVfGpxC6vkfWgP9VsT4vYQ4ZlQexHcHw==", - "license": "SEE LICENSE IN LICENSE", + "version": "17.8.2", + "resolved": "https://registry.npmjs.org/pgsql-parser/-/pgsql-parser-17.8.2.tgz", + "integrity": "sha512-/uHZL7mq3Bj23v/nDShb8gN8LwUKdejFii6IFBxRYRXxWlRrbsdky1KtevIxiVGasWZfI+E5t1//Wq+D3cVmAg==", + "license": "MIT", "dependencies": { - "libpg-query": "13.3.2", - "minimist": "^1.2.6", - "pgsql-deparser": "^13.15.0", - "pgsql-enums": "^13.10.0" - }, - "bin": { - "pgsql-parser": "main/cli.js" + "@pgsql/types": "17.6.1", + "libpg-query": "17.6.0", + "pgsql-deparser": "17.11.1" } }, "node_modules/picocolors": { @@ -6381,6 +5624,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -6430,25 +5674,6 @@ "node": ">=10" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "license": "ISC" - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -6572,20 +5797,6 @@ "node": ">=4" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6704,15 +5915,6 @@ "node": ">=10" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -6749,66 +5951,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/rollup": { "version": "4.37.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz", @@ -6900,26 +6042,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -6973,13 +6095,6 @@ "node": ">=10" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "optional": true - }, "node_modules/secure-json-parse": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", @@ -6998,12 +6113,6 @@ "node": ">=10" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC" - }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", @@ -7184,12 +6293,6 @@ "dev": true, "license": "ISC" }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -7216,44 +6319,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", - "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", - "license": "MIT", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", @@ -7318,12 +6383,6 @@ "node": ">= 10.x" } }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause" - }, "node_modules/sql-formatter": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-14.0.0.tgz", @@ -7338,18 +6397,6 @@ "sql-formatter": "bin/sql-formatter-cli.cjs" } }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -7364,19 +6411,11 @@ "dev": true, "license": "MIT" }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -7485,6 +6524,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -7555,32 +6595,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, "node_modules/tdigest": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", @@ -7616,9 +6630,9 @@ } }, "node_modules/test-exclude/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -7675,16 +6689,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/test-exclude/node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -7763,6 +6767,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -7832,12 +6837,6 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -7972,6 +6971,7 @@ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8012,24 +7012,6 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "license": "MIT" }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "license": "ISC", - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8039,12 +7021,6 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -8064,11 +7040,12 @@ } }, "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -8182,6 +7159,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8195,6 +7173,7 @@ "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "3.0.9", "@vitest/mocker": "3.0.9", @@ -8259,22 +7238,6 @@ } } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -8394,15 +7357,6 @@ "node": ">=8" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -8547,6 +7501,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, "node_modules/xtend": { @@ -8558,12 +7513,6 @@ "node": ">=0.4" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", diff --git a/package.json b/package.json index e903e455..58b95839 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "imports": { "#package.json": "./package.json" }, - "repository": "supabase/postgres-meta", + "repository": { + "url": "git+https://github.com/supabase/postgres-meta.git" + }, "scripts": { "check": "tsc -p tsconfig.json --noEmit", "clean": "rimraf dist tsconfig.tsbuildinfo", @@ -25,6 +27,7 @@ "gen:types:typescript": "PG_META_GENERATE_TYPES=typescript node --loader ts-node/esm src/server/server.ts", "gen:types:go": "PG_META_GENERATE_TYPES=go node --loader ts-node/esm src/server/server.ts", "gen:types:swift": "PG_META_GENERATE_TYPES=swift node --loader ts-node/esm src/server/server.ts", + "gen:types:python": "PG_META_GENERATE_TYPES=python node --loader ts-node/esm src/server/server.ts", "start": "node dist/server/server.js", "dev": "trap 'npm run db:clean' INT && run-s db:clean db:run && run-s dev:code", "dev:code": "nodemon --exec node --loader ts-node/esm src/server/server.ts | pino-pretty --colorize", @@ -53,7 +56,7 @@ "pg-connection-string": "^2.7.0", "pg-format": "^1.0.4", "pg-protocol": "npm:@supabase/pg-protocol@0.0.2", - "pgsql-parser": "^13.16.0", + "pgsql-parser": "^17.8.2", "pino": "^9.5.0", "postgres-array": "^3.0.1", "prettier": "^3.3.3", diff --git a/scripts/generate-python-types-test.ts b/scripts/generate-python-types-test.ts new file mode 100644 index 00000000..45111f56 --- /dev/null +++ b/scripts/generate-python-types-test.ts @@ -0,0 +1,44 @@ +#!/usr/bin/env node + +/** + * Script to generate Python types for CI validation + * This script uses the test database setup to generate Python types + */ + +import { build } from '../src/server/app.js' + +const TEST_CONNECTION_STRING = 'postgresql://postgres:postgres@localhost:5432' + +async function generatePythonTypes() { + const app = build() + + try { + const response = await app.inject({ + method: 'GET', + url: '/generators/python', + headers: { + pg: TEST_CONNECTION_STRING, + }, + query: { + access_control: 'public', + }, + }) + + if (response.statusCode !== 200) { + console.error(`Failed to generate types: ${response.statusCode}`) + console.error(response.body) + process.exit(1) + } + + // Write to stdout so it can be captured + process.stdout.write(response.body) + } catch (error) { + console.error('Error generating Python types:', error) + process.exit(1) + } finally { + await app.close() + } +} + +generatePythonTypes() + diff --git a/src/lib/Parser.ts b/src/lib/Parser.ts index abfc269d..ae1741ff 100644 --- a/src/lib/Parser.ts +++ b/src/lib/Parser.ts @@ -31,9 +31,9 @@ interface ParseReturnValues { /** * Deparses an AST into SQL string. */ -export function Deparse(parsedSql: object): DeparseReturnValues { +export async function Deparse(parsedSql: object): Promise { try { - const data = deparse(parsedSql, {}) + const data = await deparse(parsedSql, {}) return { data, error: null } } catch (error) { return { data: null, error: error as Error } diff --git a/src/lib/sql/types.sql.ts b/src/lib/sql/types.sql.ts index c230f23f..cc94ba54 100644 --- a/src/lib/sql/types.sql.ts +++ b/src/lib/sql/types.sql.ts @@ -47,7 +47,7 @@ from t.typrelid = 0 or ( select - c.relkind ${props.includeTableTypes ? `in ('c', 'r', 'v', 'm')` : `= 'c'`} + c.relkind ${props.includeTableTypes ? `in ('c', 'r', 'v', 'm', 'p')` : `= 'c'`} from pg_class c where diff --git a/src/server/routes/generators/python.ts b/src/server/routes/generators/python.ts new file mode 100644 index 00000000..706d9dd4 --- /dev/null +++ b/src/server/routes/generators/python.ts @@ -0,0 +1,33 @@ +import type { FastifyInstance } from 'fastify' +import { PostgresMeta } from '../../../lib/index.js' +import { createConnectionConfig, extractRequestForLogging } from '../../utils.js' +import { apply as applyPyTemplate } from '../../templates/python.js' +import { getGeneratorMetadata } from '../../../lib/generators.js' + +export default async (fastify: FastifyInstance) => { + fastify.get<{ + Headers: { pg: string; 'x-pg-application-name'?: string } + Querystring: { + excluded_schemas?: string + included_schemas?: string + } + }>('/', async (request, reply) => { + const config = createConnectionConfig(request) + const excludedSchemas = + request.query.excluded_schemas?.split(',').map((schema) => schema.trim()) ?? [] + const includedSchemas = + request.query.included_schemas?.split(',').map((schema) => schema.trim()) ?? [] + const pgMeta: PostgresMeta = new PostgresMeta(config) + const { data: generatorMeta, error: generatorMetaError } = await getGeneratorMetadata(pgMeta, { + includedSchemas, + excludedSchemas, + }) + if (generatorMetaError) { + request.log.error({ error: generatorMetaError, request: extractRequestForLogging(request) }) + reply.code(500) + return { error: generatorMetaError.message } + } + + return applyPyTemplate(generatorMeta) + }) +} diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts index 1532c4ea..46ffba0f 100644 --- a/src/server/routes/index.ts +++ b/src/server/routes/index.ts @@ -21,6 +21,7 @@ import ViewsRoute from './views.js' import TypeScriptTypeGenRoute from './generators/typescript.js' import GoTypeGenRoute from './generators/go.js' import SwiftTypeGenRoute from './generators/swift.js' +import PythonTypeGenRoute from './generators/python.js' import { PG_CONNECTION, CRYPTO_KEY } from '../constants.js' export default async (fastify: FastifyInstance) => { @@ -82,4 +83,5 @@ export default async (fastify: FastifyInstance) => { fastify.register(TypeScriptTypeGenRoute, { prefix: '/generators/typescript' }) fastify.register(GoTypeGenRoute, { prefix: '/generators/go' }) fastify.register(SwiftTypeGenRoute, { prefix: '/generators/swift' }) + fastify.register(PythonTypeGenRoute, { prefix: '/generators/python' }) } diff --git a/src/server/routes/query.ts b/src/server/routes/query.ts index 2cc6ad94..c6bea0c6 100644 --- a/src/server/routes/query.ts +++ b/src/server/routes/query.ts @@ -74,7 +74,7 @@ export default async (fastify: FastifyInstance) => { Headers: { pg: string; 'x-pg-application-name'?: string } Body: { ast: object } }>('/deparse', async (request, reply) => { - const { data, error } = Parser.Deparse(request.body.ast) + const { data, error } = await Parser.Deparse(request.body.ast) if (error) { request.log.error({ error, request: extractRequestForLogging(request) }) diff --git a/src/server/server.ts b/src/server/server.ts index 8b7c1c10..68fbb54c 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -18,6 +18,7 @@ import { import { apply as applyTypescriptTemplate } from './templates/typescript.js' import { apply as applyGoTemplate } from './templates/go.js' import { apply as applySwiftTemplate } from './templates/swift.js' +import { apply as applyPythonTemplate } from './templates/python.js' const logger = pino({ formatters: { @@ -143,6 +144,8 @@ async function getTypeOutput(): Promise { }) case 'go': return applyGoTemplate(config) + case 'python': + return applyPythonTemplate(config) default: throw new Error(`Unsupported language for GENERATE_TYPES: ${GENERATE_TYPES}`) } diff --git a/src/server/templates/python.ts b/src/server/templates/python.ts new file mode 100644 index 00000000..2d9459ca --- /dev/null +++ b/src/server/templates/python.ts @@ -0,0 +1,412 @@ +import type { + PostgresColumn, + PostgresMaterializedView, + PostgresSchema, + PostgresTable, + PostgresType, + PostgresView, +} from '../../lib/index.js' +import type { GeneratorMetadata } from '../../lib/generators.js' + +export const apply = ({ + schemas, + tables, + views, + materializedViews, + columns, + types, +}: GeneratorMetadata): string => { + const ctx = new PythonContext(types, columns, schemas) + // Used for efficient lookup of types by schema name + const schemasNames = new Set(schemas.map((schema) => schema.name)) + const py_tables = tables.flatMap((table) => { + const py_class_and_methods = ctx.tableToClass(table) + return py_class_and_methods + }) + const composite_types = types + // We always include system schemas, so we need to filter out types that are not in the included schemas + .filter((type) => type.attributes.length > 0 && schemasNames.has(type.schema)) + .map((type) => ctx.typeToClass(type)) + const py_views = views.map((view) => ctx.viewToClass(view)) + const py_matviews = materializedViews.map((matview) => ctx.matViewToClass(matview)) + + let output = ` +from __future__ import annotations + +import datetime +import uuid +from typing import ( + Annotated, + Any, + List, + Literal, + NotRequired, + Optional, + TypeAlias, + TypedDict, +) + +from pydantic import BaseModel, Field, Json + +${concatLines(Object.values(ctx.user_enums))} + +${concatLines(py_tables)} + +${concatLines(py_views)} + +${concatLines(py_matviews)} + +${concatLines(composite_types)} + +`.trim() + + return output +} + +interface Serializable { + serialize(): string +} + +class PythonContext { + types: { [k: string]: PostgresType } + user_enums: { [k: string]: PythonEnum } + columns: Record + schemas: { [k: string]: PostgresSchema } + + constructor(types: PostgresType[], columns: PostgresColumn[], schemas: PostgresSchema[]) { + this.schemas = Object.fromEntries(schemas.map((schema) => [schema.name, schema])) + this.types = Object.fromEntries(types.map((type) => [type.name, type])) + this.columns = columns + .sort(({ name: a }, { name: b }) => a.localeCompare(b)) + .reduce( + (acc, curr) => { + acc[curr.table_id] ??= [] + acc[curr.table_id].push(curr) + return acc + }, + {} as Record + ) + this.user_enums = Object.fromEntries( + types.filter((type) => type.enums.length > 0).map((type) => [type.name, new PythonEnum(type)]) + ) + } + + resolveTypeName(name: string): string { + if (name in this.user_enums) { + return this.user_enums[name].name + } + if (name in PY_TYPE_MAP) { + return PY_TYPE_MAP[name] + } + if (name in this.types) { + const type = this.types[name] + const schema = type!.schema + return `${formatForPyClassName(schema)}${formatForPyClassName(type.name)}` + } + return 'Any' + } + + parsePgType(pg_type: string): PythonType { + if (pg_type.startsWith('_')) { + const inner_str = pg_type.slice(1) + const inner = this.parsePgType(inner_str) + return new PythonListType(inner) + } else { + const type_name = this.resolveTypeName(pg_type) + return new PythonSimpleType(type_name) + } + } + + typeToClass(type: PostgresType): PythonBaseModel { + const types = Object.values(this.types) + const attributes = type.attributes.map((attribute) => { + const type = types.find((type) => type.id === attribute.type_id) + return { + ...attribute, + type, + } + }) + const attributeEntries: PythonBaseModelAttr[] = attributes.map((attribute) => { + const type = this.parsePgType(attribute.type!.name) + return new PythonBaseModelAttr(attribute.name, type, false) + }) + + const schema = this.schemas[type.schema] + return new PythonBaseModel(type.name, schema, attributeEntries) + } + + columnsToClassAttrs(table_id: number): PythonBaseModelAttr[] { + const attrs = this.columns[table_id] ?? [] + return attrs.map((col) => { + const type = this.parsePgType(col.format) + return new PythonBaseModelAttr(col.name, type, col.is_nullable) + }) + } + + columnsToDictAttrs(table_id: number, not_required: boolean): PythonTypedDictAttr[] { + const attrs = this.columns[table_id] ?? [] + return attrs.map((col) => { + const type = this.parsePgType(col.format) + return new PythonTypedDictAttr( + col.name, + type, + col.is_nullable, + not_required || col.is_nullable || col.is_identity || col.default_value !== null + ) + }) + } + + tableToClass(table: PostgresTable): [PythonBaseModel, PythonTypedDict, PythonTypedDict] { + const schema = this.schemas[table.schema] + const select = new PythonBaseModel(table.name, schema, this.columnsToClassAttrs(table.id)) + const insert = new PythonTypedDict( + table.name, + 'Insert', + schema, + this.columnsToDictAttrs(table.id, false) + ) + const update = new PythonTypedDict( + table.name, + 'Update', + schema, + this.columnsToDictAttrs(table.id, true) + ) + return [select, insert, update] + } + + viewToClass(view: PostgresView): PythonBaseModel { + const attributes = this.columnsToClassAttrs(view.id) + return new PythonBaseModel(view.name, this.schemas[view.schema], attributes) + } + + matViewToClass(matview: PostgresMaterializedView): PythonBaseModel { + const attributes = this.columnsToClassAttrs(matview.id) + return new PythonBaseModel(matview.name, this.schemas[matview.schema], attributes) + } +} + +class PythonEnum implements Serializable { + name: string + variants: string[] + constructor(type: PostgresType) { + this.name = `${formatForPyClassName(type.schema)}${formatForPyClassName(type.name)}` + this.variants = type.enums + } + serialize(): string { + const variants = this.variants.map((item) => `"${item}"`).join(', ') + return `${this.name}: TypeAlias = Literal[${variants}]` + } +} + +type PythonType = PythonListType | PythonSimpleType + +class PythonSimpleType implements Serializable { + name: string + constructor(name: string) { + this.name = name + } + serialize(): string { + return this.name + } +} + +class PythonListType implements Serializable { + inner: PythonType + constructor(inner: PythonType) { + this.inner = inner + } + serialize(): string { + return `List[${this.inner.serialize()}]` + } +} + +class PythonBaseModelAttr implements Serializable { + name: string + pg_name: string + py_type: PythonType + nullable: boolean + + constructor(name: string, py_type: PythonType, nullable: boolean) { + this.name = formatForPyAttributeName(name) + this.pg_name = name + this.py_type = py_type + this.nullable = nullable + } + + serialize(): string { + const py_type = this.nullable + ? `Optional[${this.py_type.serialize()}]` + : this.py_type.serialize() + return ` ${this.name}: ${py_type} = Field(alias="${this.pg_name}")` + } +} + +class PythonBaseModel implements Serializable { + name: string + table_name: string + schema: PostgresSchema + class_attributes: PythonBaseModelAttr[] + + constructor(name: string, schema: PostgresSchema, class_attributes: PythonBaseModelAttr[]) { + this.schema = schema + this.class_attributes = class_attributes + this.table_name = name + this.name = `${formatForPyClassName(schema.name)}${formatForPyClassName(name)}` + } + serialize(): string { + const attributes = + this.class_attributes.length > 0 + ? this.class_attributes.map((attr) => attr.serialize()).join('\n') + : ' pass' + return `class ${this.name}(BaseModel):\n${attributes}` + } +} + +class PythonTypedDictAttr implements Serializable { + name: string + pg_name: string + py_type: PythonType + nullable: boolean + not_required: boolean + + constructor(name: string, py_type: PythonType, nullable: boolean, required: boolean) { + this.name = formatForPyAttributeName(name) + this.pg_name = name + this.py_type = py_type + this.nullable = nullable + this.not_required = required + } + + serialize(): string { + const annotation = `Annotated[${this.py_type.serialize()}, Field(alias="${this.pg_name}")]` + const rhs = this.not_required ? `NotRequired[${annotation}]` : annotation + return ` ${this.name}: ${rhs}` + } +} + +class PythonTypedDict implements Serializable { + name: string + table_name: string + parent_class: string + schema: PostgresSchema + dict_attributes: PythonTypedDictAttr[] + operation: 'Insert' | 'Update' + + constructor( + name: string, + operation: 'Insert' | 'Update', + schema: PostgresSchema, + dict_attributes: PythonTypedDictAttr[], + parent_class: string = 'BaseModel' + ) { + this.schema = schema + this.dict_attributes = dict_attributes + this.table_name = name + this.name = `${formatForPyClassName(schema.name)}${formatForPyClassName(name)}` + this.parent_class = parent_class + this.operation = operation + } + serialize(): string { + const attributes = + this.dict_attributes.length > 0 + ? this.dict_attributes.map((attr) => attr.serialize()).join('\n') + : ' pass' + return `class ${this.name}${this.operation}(TypedDict):\n${attributes}` + } +} + +function concatLines(items: Serializable[]): string { + return items.map((item) => item.serialize()).join('\n\n') +} + +const PY_TYPE_MAP: Record = { + // Bool + bool: 'bool', + + // Numbers + int2: 'int', + int4: 'int', + int8: 'int', + float4: 'float', + float8: 'float', + numeric: 'float', + + // Strings + bytea: 'bytes', + bpchar: 'str', + varchar: 'str', + string: 'str', + date: 'datetime.date', + text: 'str', + citext: 'str', + time: 'datetime.time', + timetz: 'datetime.time', + timestamp: 'datetime.datetime', + timestamptz: 'datetime.datetime', + uuid: 'uuid.UUID', + vector: 'list[Any]', + + // JSON + json: 'Json[Any]', + jsonb: 'Json[Any]', + + // Range types (can be adjusted to more complex types if needed) + int4range: 'str', + int4multirange: 'str', + int8range: 'str', + int8multirange: 'str', + numrange: 'str', + nummultirange: 'str', + tsrange: 'str', + tsmultirange: 'str', + tstzrange: 'str', + tstzmultirange: 'str', + daterange: 'str', + datemultirange: 'str', + + // Miscellaneous types + void: 'None', + record: 'dict[str, Any]', +} as const + +/** + * Converts a Postgres name to PascalCase. + * + * @example + * ```ts + * formatForPyTypeName('pokedex') // Pokedex + * formatForPyTypeName('pokemon_center') // PokemonCenter + * formatForPyTypeName('victory-road') // VictoryRoad + * formatForPyTypeName('pokemon league') // PokemonLeague + * ``` + */ + +function formatForPyClassName(name: string): string { + return name + .split(/[^a-zA-Z0-9]/) + .map((word) => { + if (word) { + return `${word[0].toUpperCase()}${word.slice(1)}` + } else { + return '' + } + }) + .join('') +} +/** + * Converts a Postgres name to snake_case. + * + * @example + * ```ts + * formatForPyTypeName('Pokedex') // pokedex + * formatForPyTypeName('PokemonCenter') // pokemon_enter + * formatForPyTypeName('victory-road') // victory_road + * formatForPyTypeName('pokemon league') // pokemon_league + * ``` + */ +function formatForPyAttributeName(name: string): string { + return name + .split(/[^a-zA-Z0-9]+/) // Split on non-alphanumeric characters (like spaces, dashes, etc.) + .map((word) => word.toLowerCase()) // Convert each word to lowercase + .join('_') // Join with underscores +} diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts index 1b527686..75ca7de4 100644 --- a/src/server/templates/typescript.ts +++ b/src/server/templates/typescript.ts @@ -681,8 +681,10 @@ export type Database = { {} as Record ) for (const fnName in schemaFunctionsGroupedByName) { - schemaFunctionsGroupedByName[fnName].sort((a, b) => - b.fn.definition.localeCompare(a.fn.definition) + schemaFunctionsGroupedByName[fnName].sort( + (a, b) => + a.fn.argument_types.localeCompare(b.fn.argument_types) || + a.fn.return_type.localeCompare(b.fn.return_type) ) } diff --git a/test/db/00-init.sql b/test/db/00-init.sql index 64107713..8ddc77ba 100644 --- a/test/db/00-init.sql +++ b/test/db/00-init.sql @@ -9,7 +9,8 @@ CREATE TABLE public.users ( id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name text, status user_status DEFAULT 'ACTIVE', - decimal numeric + decimal numeric, + user_uuid uuid DEFAULT gen_random_uuid() ); INSERT INTO public.users (name) @@ -429,3 +430,39 @@ ROWS 1 AS $$ SELECT * FROM public.todos WHERE todos."user-id" = user_row.id LIMIT 1; $$; + +-- Function that return the created_ago computed field +CREATE OR REPLACE FUNCTION "public"."created_ago" ("public"."users_audit") RETURNS numeric LANGUAGE "sql" +SET + "search_path" TO '' AS $_$ + SELECT ROUND(EXTRACT(EPOCH FROM (NOW() - $1.created_at))); +$_$; + +-- Create a partitioned table for testing computed fields on partitioned tables +CREATE TABLE public.events ( + id bigint generated by default as identity, + created_at timestamptz default now(), + event_type text, + data jsonb, + primary key (id, created_at) +) partition by range (created_at); + +-- Create partitions for the events table +CREATE TABLE public.events_2024 PARTITION OF public.events +FOR VALUES FROM ('2024-01-01') TO ('2025-01-01'); + +CREATE TABLE public.events_2025 PARTITION OF public.events +FOR VALUES FROM ('2025-01-01') TO ('2026-01-01'); + +-- Insert some test data +INSERT INTO public.events (created_at, event_type, data) +VALUES + ('2024-06-15', 'login', '{"user": "alice"}'), + ('2025-03-20', 'logout', '{"user": "bob"}'); + +-- Function that returns computed field for partitioned table +CREATE OR REPLACE FUNCTION "public"."days_since_event" ("public"."events") RETURNS numeric LANGUAGE "sql" +SET + "search_path" TO '' AS $_$ + SELECT ROUND(EXTRACT(EPOCH FROM (NOW() - $1.created_at)) / 86400); +$_$; \ No newline at end of file diff --git a/test/lib/functions.ts b/test/lib/functions.ts index 9d6088b6..ce26e078 100644 --- a/test/lib/functions.ts +++ b/test/lib/functions.ts @@ -75,15 +75,15 @@ test('list set-returning function with single object limit', async () => { "definition": " SELECT * FROM public.users_audit WHERE user_id = user_row.id; ", - "id": 16506, + "id": 16507, "identity_argument_types": "user_row users", "is_set_returning_function": true, "language": "sql", "name": "get_user_audit_setof_single_row", "prorows": 1, "return_type": "SETOF users_audit", - "return_type_id": 16418, - "return_type_relation_id": 16416, + "return_type_id": 16419, + "return_type_relation_id": 16417, "schema": "public", "security_definer": false, }, @@ -118,15 +118,15 @@ test('list set-returning function with multiples definitions', async () => { "definition": " SELECT * FROM public.todos WHERE "user-id" = user_row.id; ", - "id": 16509, + "id": 16510, "identity_argument_types": "user_row users", "is_set_returning_function": true, "language": "sql", "name": "get_todos_setof_rows", "prorows": 1000, "return_type": "SETOF todos", - "return_type_id": 16404, - "return_type_relation_id": 16402, + "return_type_id": 16405, + "return_type_relation_id": 16403, "schema": "public", "security_definer": false, }, @@ -136,7 +136,7 @@ test('list set-returning function with multiples definitions', async () => { "has_default": false, "mode": "in", "name": "todo_row", - "type_id": 16404, + "type_id": 16405, }, ], "argument_types": "todo_row todos", @@ -153,15 +153,15 @@ test('list set-returning function with multiples definitions', async () => { "definition": " SELECT * FROM public.todos WHERE "user-id" = todo_row."user-id"; ", - "id": 16510, + "id": 16511, "identity_argument_types": "todo_row todos", "is_set_returning_function": true, "language": "sql", "name": "get_todos_setof_rows", "prorows": 1000, "return_type": "SETOF todos", - "return_type_id": 16404, - "return_type_relation_id": 16402, + "return_type_id": 16405, + "return_type_relation_id": 16403, "schema": "public", "security_definer": false, }, diff --git a/test/lib/tables.ts b/test/lib/tables.ts index 677204fc..a0dfbaad 100644 --- a/test/lib/tables.ts +++ b/test/lib/tables.ts @@ -117,6 +117,24 @@ test('list', async () => { "schema": "public", "table": "users", }, + { + "check": null, + "comment": null, + "data_type": "uuid", + "default_value": "gen_random_uuid()", + "enums": [], + "format": "uuid", + "identity_generation": null, + "is_generated": false, + "is_identity": false, + "is_nullable": true, + "is_unique": false, + "is_updatable": true, + "name": "user_uuid", + "ordinal_position": 5, + "schema": "public", + "table": "users", + }, ], "comment": null, "dead_rows_estimate": Any, diff --git a/test/lib/types.ts b/test/lib/types.ts index 349a1b80..b256697e 100644 --- a/test/lib/types.ts +++ b/test/lib/types.ts @@ -74,7 +74,7 @@ test('list types with include Table Types', async () => { "id": Any, "name": "todos", "schema": "public", - "type_relation_id": 16402, + "type_relation_id": 16403, } ` ) diff --git a/test/lib/views.ts b/test/lib/views.ts index 275eb4a3..e623e14b 100644 --- a/test/lib/views.ts +++ b/test/lib/views.ts @@ -15,7 +15,7 @@ test('list', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16423.1", + "id": "16424.1", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -26,7 +26,7 @@ test('list', async () => { "ordinal_position": 1, "schema": "public", "table": "todos_view", - "table_id": 16423, + "table_id": 16424, }, { "check": null, @@ -35,7 +35,7 @@ test('list', async () => { "default_value": null, "enums": [], "format": "text", - "id": "16423.2", + "id": "16424.2", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -46,7 +46,7 @@ test('list', async () => { "ordinal_position": 2, "schema": "public", "table": "todos_view", - "table_id": 16423, + "table_id": 16424, }, { "check": null, @@ -55,7 +55,7 @@ test('list', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16423.3", + "id": "16424.3", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -66,7 +66,7 @@ test('list', async () => { "ordinal_position": 3, "schema": "public", "table": "todos_view", - "table_id": 16423, + "table_id": 16424, }, ], "comment": null, @@ -112,7 +112,7 @@ test('retrieve', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16423.1", + "id": "16424.1", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -123,7 +123,7 @@ test('retrieve', async () => { "ordinal_position": 1, "schema": "public", "table": "todos_view", - "table_id": 16423, + "table_id": 16424, }, { "check": null, @@ -132,7 +132,7 @@ test('retrieve', async () => { "default_value": null, "enums": [], "format": "text", - "id": "16423.2", + "id": "16424.2", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -143,7 +143,7 @@ test('retrieve', async () => { "ordinal_position": 2, "schema": "public", "table": "todos_view", - "table_id": 16423, + "table_id": 16424, }, { "check": null, @@ -152,7 +152,7 @@ test('retrieve', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16423.3", + "id": "16424.3", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -163,7 +163,7 @@ test('retrieve', async () => { "ordinal_position": 3, "schema": "public", "table": "todos_view", - "table_id": 16423, + "table_id": 16424, }, ], "comment": null, diff --git a/test/server/indexes.ts b/test/server/indexes.ts index b3fb7f0c..1ad4d0a2 100644 --- a/test/server/indexes.ts +++ b/test/server/indexes.ts @@ -22,7 +22,7 @@ test('list indexes', async () => { 0, ], "comment": null, - "id": 16399, + "id": 16400, "index_attributes": [ { "attribute_name": "id", @@ -57,7 +57,7 @@ test('list indexes', async () => { }) test('retrieve index', async () => { - const res = await app.inject({ method: 'GET', path: '/indexes/16399' }) + const res = await app.inject({ method: 'GET', path: '/indexes/16400' }) const index = res.json() expect(index).toMatchInlineSnapshot( ` @@ -71,7 +71,7 @@ test('retrieve index', async () => { 0, ], "comment": null, - "id": 16399, + "id": 16400, "index_attributes": [ { "attribute_name": "id", diff --git a/test/server/query.ts b/test/server/query.ts index 2cd86f52..01f9cc92 100644 --- a/test/server/query.ts +++ b/test/server/query.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest' -import { app } from './utils' +import { app, normalizeUuids } from './utils' test('query', async () => { const res = await app.inject({ @@ -7,19 +7,21 @@ test('query', async () => { path: '/query', payload: { query: 'SELECT * FROM users' }, }) - expect(res.json()).toMatchInlineSnapshot(` + expect(normalizeUuids(res.json())).toMatchInlineSnapshot(` [ { "decimal": null, "id": 1, "name": "Joe Bloggs", "status": "ACTIVE", + "user_uuid": "00000000-0000-0000-0000-000000000000", }, { "decimal": null, "id": 2, "name": "Jane Doe", "status": "ACTIVE", + "user_uuid": "00000000-0000-0000-0000-000000000000", }, ] `) @@ -56,9 +58,9 @@ test('parser select statements', async () => { payload: { query: 'SELECT id, name FROM users where user_id = 1234' }, }) expect(res.json()).toMatchInlineSnapshot(` - [ - { - "RawStmt": { + { + "stmts": [ + { "stmt": { "SelectStmt": { "fromClause": [ @@ -82,7 +84,7 @@ test('parser select statements', async () => { "fields": [ { "String": { - "str": "id", + "sval": "id", }, }, ], @@ -99,7 +101,7 @@ test('parser select statements', async () => { "fields": [ { "String": { - "str": "name", + "sval": "name", }, }, ], @@ -117,7 +119,7 @@ test('parser select statements', async () => { "fields": [ { "String": { - "str": "user_id", + "sval": "user_id", }, }, ], @@ -128,28 +130,26 @@ test('parser select statements', async () => { "name": [ { "String": { - "str": "=", + "sval": "=", }, }, ], "rexpr": { "A_Const": { - "location": 43, - "val": { - "Integer": { - "ival": 1234, - }, + "ival": { + "ival": 1234, }, + "location": 43, }, }, }, }, }, }, - "stmt_location": 0, }, - }, - ] + ], + "version": 170004, + } `) }) @@ -163,7 +163,12 @@ test('parser comments', async () => { `, }, }) - expect(res.json()).toMatchInlineSnapshot(`[]`) + expect(res.json()).toMatchInlineSnapshot(` + { + "stmts": [], + "version": 170004, + } + `) }) test('parser create schema', async () => { @@ -177,9 +182,9 @@ create schema if not exists test_schema; }, }) expect(res.json()).toMatchInlineSnapshot(` - [ - { - "RawStmt": { + { + "stmts": [ + { "stmt": { "CreateSchemaStmt": { "if_not_exists": true, @@ -187,10 +192,10 @@ create schema if not exists test_schema; }, }, "stmt_len": 40, - "stmt_location": 0, }, - }, - ] + ], + "version": 170004, + } `) }) @@ -211,9 +216,9 @@ CREATE TABLE table_name ( }, }) expect(res.json()).toMatchInlineSnapshot(` - [ - { - "RawStmt": { + { + "stmts": [ + { "stmt": { "CreateStmt": { "oncommit": "ONCOMMIT_NOOP", @@ -249,12 +254,12 @@ CREATE TABLE table_name ( "names": [ { "String": { - "str": "pg_catalog", + "sval": "pg_catalog", }, }, { "String": { - "str": "int8", + "sval": "int8", }, }, ], @@ -278,10 +283,8 @@ CREATE TABLE table_name ( "arg": { "A_Const": { "location": 141, - "val": { - "String": { - "str": "utc", - }, + "sval": { + "sval": "utc", }, }, }, @@ -291,7 +294,7 @@ CREATE TABLE table_name ( "names": [ { "String": { - "str": "text", + "sval": "text", }, }, ], @@ -301,10 +304,11 @@ CREATE TABLE table_name ( }, { "FuncCall": { + "funcformat": "COERCE_EXPLICIT_CALL", "funcname": [ { "String": { - "str": "now", + "sval": "now", }, }, ], @@ -312,10 +316,11 @@ CREATE TABLE table_name ( }, }, ], + "funcformat": "COERCE_EXPLICIT_CALL", "funcname": [ { "String": { - "str": "timezone", + "sval": "timezone", }, }, ], @@ -338,12 +343,12 @@ CREATE TABLE table_name ( "names": [ { "String": { - "str": "pg_catalog", + "sval": "pg_catalog", }, }, { "String": { - "str": "timestamptz", + "sval": "timestamptz", }, }, ], @@ -367,10 +372,8 @@ CREATE TABLE table_name ( "arg": { "A_Const": { "location": 226, - "val": { - "String": { - "str": "utc", - }, + "sval": { + "sval": "utc", }, }, }, @@ -380,7 +383,7 @@ CREATE TABLE table_name ( "names": [ { "String": { - "str": "text", + "sval": "text", }, }, ], @@ -390,10 +393,11 @@ CREATE TABLE table_name ( }, { "FuncCall": { + "funcformat": "COERCE_EXPLICIT_CALL", "funcname": [ { "String": { - "str": "now", + "sval": "now", }, }, ], @@ -401,10 +405,11 @@ CREATE TABLE table_name ( }, }, ], + "funcformat": "COERCE_EXPLICIT_CALL", "funcname": [ { "String": { - "str": "timezone", + "sval": "timezone", }, }, ], @@ -427,12 +432,12 @@ CREATE TABLE table_name ( "names": [ { "String": { - "str": "pg_catalog", + "sval": "pg_catalog", }, }, { "String": { - "str": "timestamptz", + "sval": "timestamptz", }, }, ], @@ -450,7 +455,7 @@ CREATE TABLE table_name ( "names": [ { "String": { - "str": "jsonb", + "sval": "jsonb", }, }, ], @@ -468,7 +473,7 @@ CREATE TABLE table_name ( "names": [ { "String": { - "str": "text", + "sval": "text", }, }, ], @@ -480,10 +485,10 @@ CREATE TABLE table_name ( }, }, "stmt_len": 283, - "stmt_location": 0, }, - }, - ] + ], + "version": 170004, + } `) const deparse = await app.inject({ @@ -493,11 +498,11 @@ CREATE TABLE table_name ( }) expect(deparse.body).toMatchInlineSnapshot(` "CREATE TABLE table_name ( - id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - inserted_at pg_catalog.timestamptz DEFAULT ( timezone('utc'::text, now()) ) NOT NULL, - updated_at pg_catalog.timestamptz DEFAULT ( timezone('utc'::text, now()) ) NOT NULL, - data jsonb, - name text + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + inserted_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + updated_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + data jsonb, + name text );" `) }) @@ -755,13 +760,14 @@ test('parameter binding with positional parameters', async () => { parameters: [1, 'ACTIVE'], }, }) - expect(res.json()).toMatchInlineSnapshot(` + expect(normalizeUuids(res.json())).toMatchInlineSnapshot(` [ { "decimal": null, "id": 1, "name": "Joe Bloggs", "status": "ACTIVE", + "user_uuid": "00000000-0000-0000-0000-000000000000", }, ] `) diff --git a/test/server/typegen.ts b/test/server/typegen.ts index f101adde..d71b468e 100644 --- a/test/server/typegen.ts +++ b/test/server/typegen.ts @@ -37,6 +37,70 @@ test('typegen: typescript', async () => { Update: {} Relationships: [] } + events: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + days_since_event: number | null + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + events_2024: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + events_2025: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } foreign_table: { Row: { id: number @@ -243,6 +307,7 @@ test('typegen: typescript', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null test_unnamed_row_composite: | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] | null @@ -257,12 +322,14 @@ test('typegen: typescript', async () => { id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -272,6 +339,7 @@ test('typegen: typescript', async () => { id: number previous_value: Json | null user_id: number | null + created_ago: number | null } Insert: { created_at?: string | null @@ -428,18 +496,21 @@ test('typegen: typescript', async () => { id: number | null name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } Insert: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -473,6 +544,18 @@ test('typegen: typescript', async () => { error: true } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } + created_ago: { + Args: { "": Database["public"]["Tables"]["users_audit"]["Row"] } + Returns: { + error: true + } & "the function public.created_ago with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + days_since_event: { + Args: { "": Database["public"]["Tables"]["events"]["Row"] } + Returns: { + error: true + } & "the function public.days_since_event with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { @@ -498,6 +581,7 @@ test('typegen: typescript', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "*" @@ -513,6 +597,7 @@ test('typegen: typescript', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" @@ -528,6 +613,7 @@ test('typegen: typescript', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "todos" @@ -590,9 +676,7 @@ test('typegen: typescript', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -601,7 +685,7 @@ test('typegen: typescript', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -624,7 +708,9 @@ test('typegen: typescript', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -633,7 +719,7 @@ test('typegen: typescript', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -655,16 +741,14 @@ test('typegen: typescript', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -685,14 +769,16 @@ test('typegen: typescript', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -700,28 +786,28 @@ test('typegen: typescript', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -788,32 +874,35 @@ test('typegen: typescript', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -832,22 +921,20 @@ test('typegen: typescript', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -881,28 +968,28 @@ test('typegen: typescript', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -1122,6 +1209,70 @@ test('typegen w/ one-to-one relationships', async () => { Update: {} Relationships: [] } + events: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + days_since_event: number | null + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + events_2024: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + events_2025: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } foreign_table: { Row: { id: number @@ -1341,6 +1492,7 @@ test('typegen w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null test_unnamed_row_composite: | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] | null @@ -1355,12 +1507,14 @@ test('typegen w/ one-to-one relationships', async () => { id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -1370,6 +1524,7 @@ test('typegen w/ one-to-one relationships', async () => { id: number previous_value: Json | null user_id: number | null + created_ago: number | null } Insert: { created_at?: string | null @@ -1538,18 +1693,21 @@ test('typegen w/ one-to-one relationships', async () => { id: number | null name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } Insert: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -1583,6 +1741,18 @@ test('typegen w/ one-to-one relationships', async () => { error: true } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } + created_ago: { + Args: { "": Database["public"]["Tables"]["users_audit"]["Row"] } + Returns: { + error: true + } & "the function public.created_ago with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + days_since_event: { + Args: { "": Database["public"]["Tables"]["events"]["Row"] } + Returns: { + error: true + } & "the function public.days_since_event with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { @@ -1608,6 +1778,7 @@ test('typegen w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "*" @@ -1623,6 +1794,7 @@ test('typegen w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" @@ -1638,6 +1810,7 @@ test('typegen w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "todos" @@ -1700,9 +1873,7 @@ test('typegen w/ one-to-one relationships', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -1711,7 +1882,7 @@ test('typegen w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -1734,7 +1905,9 @@ test('typegen w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -1743,7 +1916,7 @@ test('typegen w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -1765,16 +1938,14 @@ test('typegen w/ one-to-one relationships', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -1795,14 +1966,16 @@ test('typegen w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -1810,28 +1983,28 @@ test('typegen w/ one-to-one relationships', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -1898,32 +2071,35 @@ test('typegen w/ one-to-one relationships', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -1942,22 +2118,20 @@ test('typegen w/ one-to-one relationships', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -1991,28 +2165,28 @@ test('typegen w/ one-to-one relationships', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -2232,36 +2406,100 @@ test('typegen: typescript w/ one-to-one relationships', async () => { Update: {} Relationships: [] } - foreign_table: { + events: { Row: { + created_at: string + data: Json | null + event_type: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + days_since_event: number | null } Insert: { - id: number - name?: string | null - status?: Database["public"]["Enums"]["user_status"] | null + created_at?: string + data?: Json | null + event_type?: string | null + id?: number } Update: { + created_at?: string + data?: Json | null + event_type?: string | null id?: number - name?: string | null - status?: Database["public"]["Enums"]["user_status"] | null } Relationships: [] } - memes: { + events_2024: { Row: { - category: number | null created_at: string + data: Json | null + event_type: string | null id: number - metadata: Json | null - name: string - status: Database["public"]["Enums"]["meme_status"] | null } Insert: { - category?: number | null - created_at: string + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + events_2025: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + foreign_table: { + Row: { + id: number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + } + Insert: { + id: number + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Update: { + id?: number + name?: string | null + status?: Database["public"]["Enums"]["user_status"] | null + } + Relationships: [] + } + memes: { + Row: { + category: number | null + created_at: string + id: number + metadata: Json | null + name: string + status: Database["public"]["Enums"]["meme_status"] | null + } + Insert: { + category?: number | null + created_at: string id?: number metadata?: Json | null name: string @@ -2451,6 +2689,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null test_unnamed_row_composite: | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] | null @@ -2465,12 +2704,14 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -2480,6 +2721,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number previous_value: Json | null user_id: number | null + created_ago: number | null } Insert: { created_at?: string | null @@ -2648,18 +2890,21 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number | null name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } Insert: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -2693,6 +2938,18 @@ test('typegen: typescript w/ one-to-one relationships', async () => { error: true } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } + created_ago: { + Args: { "": Database["public"]["Tables"]["users_audit"]["Row"] } + Returns: { + error: true + } & "the function public.created_ago with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + days_since_event: { + Args: { "": Database["public"]["Tables"]["events"]["Row"] } + Returns: { + error: true + } & "the function public.days_since_event with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { @@ -2718,6 +2975,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "*" @@ -2733,6 +2991,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" @@ -2748,6 +3007,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "todos" @@ -2810,9 +3070,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -2821,7 +3079,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -2844,7 +3102,9 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -2853,7 +3113,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -2875,16 +3135,14 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -2905,14 +3163,16 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -2920,28 +3180,28 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -3008,32 +3268,35 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -3052,22 +3315,20 @@ test('typegen: typescript w/ one-to-one relationships', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -3101,28 +3362,28 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -3347,6 +3608,70 @@ test('typegen: typescript w/ postgrestVersion', async () => { Update: {} Relationships: [] } + events: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + days_since_event: number | null + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + events_2024: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } + events_2025: { + Row: { + created_at: string + data: Json | null + event_type: string | null + id: number + } + Insert: { + created_at?: string + data?: Json | null + event_type?: string | null + id: number + } + Update: { + created_at?: string + data?: Json | null + event_type?: string | null + id?: number + } + Relationships: [] + } foreign_table: { Row: { id: number @@ -3566,6 +3891,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null test_unnamed_row_composite: | Database["public"]["CompositeTypes"]["composite_type_with_array_attribute"] | null @@ -3580,12 +3906,14 @@ test('typegen: typescript w/ postgrestVersion', async () => { id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -3595,6 +3923,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number previous_value: Json | null user_id: number | null + created_ago: number | null } Insert: { created_at?: string | null @@ -3763,18 +4092,21 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number | null name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } Insert: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Update: { decimal?: number | null id?: number | null name?: string | null status?: Database["public"]["Enums"]["user_status"] | null + user_uuid?: string | null } Relationships: [] } @@ -3808,6 +4140,18 @@ test('typegen: typescript w/ postgrestVersion', async () => { error: true } & "the function public.blurb_varchar with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" } + created_ago: { + Args: { "": Database["public"]["Tables"]["users_audit"]["Row"] } + Returns: { + error: true + } & "the function public.created_ago with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } + days_since_event: { + Args: { "": Database["public"]["Tables"]["events"]["Row"] } + Returns: { + error: true + } & "the function public.days_since_event with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache" + } details_is_long: { Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { @@ -3833,6 +4177,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "*" @@ -3848,6 +4193,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" @@ -3863,6 +4209,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { id: number name: string | null status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null } SetofOptions: { from: "todos" @@ -3925,9 +4272,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -3936,7 +4281,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -3959,7 +4304,9 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -3968,7 +4315,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -3990,16 +4337,14 @@ test('typegen: typescript w/ postgrestVersion', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -4020,14 +4365,16 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -4035,28 +4382,28 @@ test('typegen: typescript w/ postgrestVersion', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -4123,32 +4470,35 @@ test('typegen: typescript w/ postgrestVersion', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null + user_uuid: string | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -4167,22 +4517,20 @@ test('typegen: typescript w/ postgrestVersion', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -4216,28 +4564,28 @@ test('typegen: typescript w/ postgrestVersion', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -4684,30 +5032,152 @@ test('typegen: typescript consistent types definitions orders', async () => { expect(firstCall).toEqual(secondCall) }) -test('typegen: go', async () => { - const { body } = await app.inject({ method: 'GET', path: '/generators/go' }) - expect(body).toMatchInlineSnapshot(` - "package database - - type PublicUsersSelect struct { - Decimal *float64 \`json:"decimal"\` - Id int64 \`json:"id"\` - Name *string \`json:"name"\` - Status *string \`json:"status"\` - } +test('typegen: typescript function override order stability', async () => { + // Helper function to clean up test entities + const cleanupTestEntities = async () => { + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Drop functions with all possible signatures + DROP FUNCTION IF EXISTS test_func_override(integer, text) CASCADE; + DROP FUNCTION IF EXISTS test_func_override(text, integer) CASCADE; + DROP FUNCTION IF EXISTS test_func_override(boolean, integer, text) CASCADE; + DROP FUNCTION IF EXISTS test_func_override(text, boolean) CASCADE; + `, + }, + }) + } + + // Clean up any existing test entities + await cleanupTestEntities() + + // === FIRST ROUND: Create function overrides in order 1 === + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Create function overrides in specific order + CREATE FUNCTION test_func_override(param_a integer, param_b text) + RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a text, param_b integer) + RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text) + RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a text, param_b boolean) + RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE; + `, + }, + }) + + // Generate types for first configuration + const { body: firstCall } = await app.inject({ + method: 'GET', + path: '/generators/typescript', + query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' }, + }) + + // === SECOND ROUND: Modify function definitions without changing signatures === + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Modify function definitions (using CREATE OR REPLACE) + -- This should preserve the order + CREATE OR REPLACE FUNCTION test_func_override(param_a integer, param_b text) + RETURNS integer AS 'SELECT param_a + 100' LANGUAGE sql IMMUTABLE; + + CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b integer) + RETURNS text AS 'SELECT param_a || ''_'' || param_b::text' LANGUAGE sql IMMUTABLE; + + CREATE OR REPLACE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text) + RETURNS boolean AS 'SELECT NOT param_a' LANGUAGE sql IMMUTABLE; + + CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b boolean) + RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a || ''_true'' ELSE ''false'' END' LANGUAGE sql IMMUTABLE; + `, + }, + }) + + // Generate types for second configuration (after modifying definitions) + const { body: secondCall } = await app.inject({ + method: 'GET', + path: '/generators/typescript', + query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' }, + }) + + // === THIRD ROUND: Drop and recreate in different order === + await cleanupTestEntities() + + // Create functions in reverse order + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Create function overrides in reverse order + CREATE FUNCTION test_func_override(param_a text, param_b boolean) + RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text) + RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a text, param_b integer) + RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a integer, param_b text) + RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE; + `, + }, + }) + + // Generate types for third configuration (recreated in different order) + const { body: thirdCall } = await app.inject({ + method: 'GET', + path: '/generators/typescript', + query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' }, + }) + + // Clean up test entities + await cleanupTestEntities() + + expect(firstCall).toEqual(secondCall) + expect(secondCall).toEqual(thirdCall) +}) + +test('typegen: go', async () => { + const { body } = await app.inject({ method: 'GET', path: '/generators/go' }) + expect(body).toMatchInlineSnapshot(` + "package database + + type PublicUsersSelect struct { + Decimal *float64 \`json:"decimal"\` + Id int64 \`json:"id"\` + Name *string \`json:"name"\` + Status *string \`json:"status"\` + UserUuid *string \`json:"user_uuid"\` + } type PublicUsersInsert struct { - Decimal *float64 \`json:"decimal"\` - Id *int64 \`json:"id"\` - Name *string \`json:"name"\` - Status *string \`json:"status"\` + Decimal *float64 \`json:"decimal"\` + Id *int64 \`json:"id"\` + Name *string \`json:"name"\` + Status *string \`json:"status"\` + UserUuid *string \`json:"user_uuid"\` } type PublicUsersUpdate struct { - Decimal *float64 \`json:"decimal"\` - Id *int64 \`json:"id"\` - Name *string \`json:"name"\` - Status *string \`json:"status"\` + Decimal *float64 \`json:"decimal"\` + Id *int64 \`json:"id"\` + Name *string \`json:"name"\` + Status *string \`json:"status"\` + UserUuid *string \`json:"user_uuid"\` } type PublicTodosSelect struct { @@ -4806,6 +5276,69 @@ test('typegen: go', async () => { OtherId *int64 \`json:"other_id"\` } + type PublicEventsSelect struct { + CreatedAt string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id int64 \`json:"id"\` + } + + type PublicEventsInsert struct { + CreatedAt *string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id *int64 \`json:"id"\` + } + + type PublicEventsUpdate struct { + CreatedAt *string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id *int64 \`json:"id"\` + } + + type PublicEvents2024Select struct { + CreatedAt string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id int64 \`json:"id"\` + } + + type PublicEvents2024Insert struct { + CreatedAt *string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id int64 \`json:"id"\` + } + + type PublicEvents2024Update struct { + CreatedAt *string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id *int64 \`json:"id"\` + } + + type PublicEvents2025Select struct { + CreatedAt string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id int64 \`json:"id"\` + } + + type PublicEvents2025Insert struct { + CreatedAt *string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id int64 \`json:"id"\` + } + + type PublicEvents2025Update struct { + CreatedAt *string \`json:"created_at"\` + Data interface{} \`json:"data"\` + EventType *string \`json:"event_type"\` + Id *int64 \`json:"id"\` + } + type PublicCategorySelect struct { Id int32 \`json:"id"\` Name string \`json:"name"\` @@ -4859,10 +5392,11 @@ test('typegen: go', async () => { } type PublicUsersViewSelect struct { - Decimal *float64 \`json:"decimal"\` - Id *int64 \`json:"id"\` - Name *string \`json:"name"\` - Status *string \`json:"status"\` + Decimal *float64 \`json:"decimal"\` + Id *int64 \`json:"id"\` + Name *string \`json:"name"\` + Status *string \`json:"status"\` + UserUuid *string \`json:"user_uuid"\` } type PublicUserTodosSummaryViewSelect struct { @@ -4942,6 +5476,114 @@ test('typegen: swift', async () => { } internal struct EmptyUpdate: Codable, Hashable, Sendable { } + internal struct EventsSelect: Codable, Hashable, Sendable, Identifiable { + internal let createdAt: String + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64 + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct EventsInsert: Codable, Hashable, Sendable, Identifiable { + internal let createdAt: String? + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64? + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct EventsUpdate: Codable, Hashable, Sendable, Identifiable { + internal let createdAt: String? + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64? + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct Events2024Select: Codable, Hashable, Sendable { + internal let createdAt: String + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64 + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct Events2024Insert: Codable, Hashable, Sendable { + internal let createdAt: String? + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64 + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct Events2024Update: Codable, Hashable, Sendable { + internal let createdAt: String? + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64? + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct Events2025Select: Codable, Hashable, Sendable { + internal let createdAt: String + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64 + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct Events2025Insert: Codable, Hashable, Sendable { + internal let createdAt: String? + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64 + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + internal struct Events2025Update: Codable, Hashable, Sendable { + internal let createdAt: String? + internal let data: AnyJSON? + internal let eventType: String? + internal let id: Int64? + internal enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } internal struct ForeignTableSelect: Codable, Hashable, Sendable { internal let id: Int64 internal let name: String? @@ -5130,11 +5772,13 @@ test('typegen: swift', async () => { internal let id: Int64 internal let name: String? internal let status: UserStatus? + internal let userUuid: UUID? internal enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } internal struct UsersInsert: Codable, Hashable, Sendable, Identifiable { @@ -5142,11 +5786,13 @@ test('typegen: swift', async () => { internal let id: Int64? internal let name: String? internal let status: UserStatus? + internal let userUuid: UUID? internal enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } internal struct UsersUpdate: Codable, Hashable, Sendable, Identifiable { @@ -5154,11 +5800,13 @@ test('typegen: swift', async () => { internal let id: Int64? internal let name: String? internal let status: UserStatus? + internal let userUuid: UUID? internal enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } internal struct UsersAuditSelect: Codable, Hashable, Sendable, Identifiable { @@ -5242,11 +5890,13 @@ test('typegen: swift', async () => { internal let id: Int64? internal let name: String? internal let status: UserStatus? + internal let userUuid: UUID? internal enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } internal struct UsersViewWithMultipleRefsToUsersSelect: Codable, Hashable, Sendable { @@ -5327,6 +5977,114 @@ test('typegen: swift w/ public access control', async () => { } public struct EmptyUpdate: Codable, Hashable, Sendable { } + public struct EventsSelect: Codable, Hashable, Sendable, Identifiable { + public let createdAt: String + public let data: AnyJSON? + public let eventType: String? + public let id: Int64 + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct EventsInsert: Codable, Hashable, Sendable, Identifiable { + public let createdAt: String? + public let data: AnyJSON? + public let eventType: String? + public let id: Int64? + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct EventsUpdate: Codable, Hashable, Sendable, Identifiable { + public let createdAt: String? + public let data: AnyJSON? + public let eventType: String? + public let id: Int64? + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct Events2024Select: Codable, Hashable, Sendable { + public let createdAt: String + public let data: AnyJSON? + public let eventType: String? + public let id: Int64 + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct Events2024Insert: Codable, Hashable, Sendable { + public let createdAt: String? + public let data: AnyJSON? + public let eventType: String? + public let id: Int64 + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct Events2024Update: Codable, Hashable, Sendable { + public let createdAt: String? + public let data: AnyJSON? + public let eventType: String? + public let id: Int64? + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct Events2025Select: Codable, Hashable, Sendable { + public let createdAt: String + public let data: AnyJSON? + public let eventType: String? + public let id: Int64 + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct Events2025Insert: Codable, Hashable, Sendable { + public let createdAt: String? + public let data: AnyJSON? + public let eventType: String? + public let id: Int64 + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } + public struct Events2025Update: Codable, Hashable, Sendable { + public let createdAt: String? + public let data: AnyJSON? + public let eventType: String? + public let id: Int64? + public enum CodingKeys: String, CodingKey { + case createdAt = "created_at" + case data = "data" + case eventType = "event_type" + case id = "id" + } + } public struct ForeignTableSelect: Codable, Hashable, Sendable { public let id: Int64 public let name: String? @@ -5515,11 +6273,13 @@ test('typegen: swift w/ public access control', async () => { public let id: Int64 public let name: String? public let status: UserStatus? + public let userUuid: UUID? public enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } public struct UsersInsert: Codable, Hashable, Sendable, Identifiable { @@ -5527,11 +6287,13 @@ test('typegen: swift w/ public access control', async () => { public let id: Int64? public let name: String? public let status: UserStatus? + public let userUuid: UUID? public enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } public struct UsersUpdate: Codable, Hashable, Sendable, Identifiable { @@ -5539,11 +6301,13 @@ test('typegen: swift w/ public access control', async () => { public let id: Int64? public let name: String? public let status: UserStatus? + public let userUuid: UUID? public enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } public struct UsersAuditSelect: Codable, Hashable, Sendable, Identifiable { @@ -5627,11 +6391,13 @@ test('typegen: swift w/ public access control', async () => { public let id: Int64? public let name: String? public let status: UserStatus? + public let userUuid: UUID? public enum CodingKeys: String, CodingKey { case decimal = "decimal" case id = "id" case name = "name" case status = "status" + case userUuid = "user_uuid" } } public struct UsersViewWithMultipleRefsToUsersSelect: Codable, Hashable, Sendable { @@ -5661,3 +6427,335 @@ test('typegen: swift w/ public access control', async () => { }" `) }) + +test('typegen: python', async () => { + const { body } = await app.inject({ + method: 'GET', + path: '/generators/python', + query: { access_control: 'public' }, + }) + expect(body).toMatchInlineSnapshot(` + "from __future__ import annotations + + import datetime + import uuid + from typing import ( + Annotated, + Any, + List, + Literal, + NotRequired, + Optional, + TypeAlias, + TypedDict, + ) + + from pydantic import BaseModel, Field, Json + + PublicUserStatus: TypeAlias = Literal["ACTIVE", "INACTIVE"] + + PublicMemeStatus: TypeAlias = Literal["new", "old", "retired"] + + class PublicUsers(BaseModel): + decimal: Optional[float] = Field(alias="decimal") + id: int = Field(alias="id") + name: Optional[str] = Field(alias="name") + status: Optional[PublicUserStatus] = Field(alias="status") + user_uuid: Optional[uuid.UUID] = Field(alias="user_uuid") + + class PublicUsersInsert(TypedDict): + decimal: NotRequired[Annotated[float, Field(alias="decimal")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + name: NotRequired[Annotated[str, Field(alias="name")]] + status: NotRequired[Annotated[PublicUserStatus, Field(alias="status")]] + user_uuid: NotRequired[Annotated[uuid.UUID, Field(alias="user_uuid")]] + + class PublicUsersUpdate(TypedDict): + decimal: NotRequired[Annotated[float, Field(alias="decimal")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + name: NotRequired[Annotated[str, Field(alias="name")]] + status: NotRequired[Annotated[PublicUserStatus, Field(alias="status")]] + user_uuid: NotRequired[Annotated[uuid.UUID, Field(alias="user_uuid")]] + + class PublicTodos(BaseModel): + details: Optional[str] = Field(alias="details") + id: int = Field(alias="id") + user_id: int = Field(alias="user-id") + + class PublicTodosInsert(TypedDict): + details: NotRequired[Annotated[str, Field(alias="details")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + user_id: Annotated[int, Field(alias="user-id")] + + class PublicTodosUpdate(TypedDict): + details: NotRequired[Annotated[str, Field(alias="details")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + user_id: NotRequired[Annotated[int, Field(alias="user-id")]] + + class PublicUsersAudit(BaseModel): + created_at: Optional[datetime.datetime] = Field(alias="created_at") + id: int = Field(alias="id") + previous_value: Optional[Json[Any]] = Field(alias="previous_value") + user_id: Optional[int] = Field(alias="user_id") + + class PublicUsersAuditInsert(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + previous_value: NotRequired[Annotated[Json[Any], Field(alias="previous_value")]] + user_id: NotRequired[Annotated[int, Field(alias="user_id")]] + + class PublicUsersAuditUpdate(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + previous_value: NotRequired[Annotated[Json[Any], Field(alias="previous_value")]] + user_id: NotRequired[Annotated[int, Field(alias="user_id")]] + + class PublicUserDetails(BaseModel): + details: Optional[str] = Field(alias="details") + user_id: int = Field(alias="user_id") + + class PublicUserDetailsInsert(TypedDict): + details: NotRequired[Annotated[str, Field(alias="details")]] + user_id: Annotated[int, Field(alias="user_id")] + + class PublicUserDetailsUpdate(TypedDict): + details: NotRequired[Annotated[str, Field(alias="details")]] + user_id: NotRequired[Annotated[int, Field(alias="user_id")]] + + class PublicEmpty(BaseModel): + pass + + class PublicEmptyInsert(TypedDict): + pass + + class PublicEmptyUpdate(TypedDict): + pass + + class PublicTableWithOtherTablesRowType(BaseModel): + col1: Optional[PublicUserDetails] = Field(alias="col1") + col2: Optional[PublicAView] = Field(alias="col2") + + class PublicTableWithOtherTablesRowTypeInsert(TypedDict): + col1: NotRequired[Annotated[PublicUserDetails, Field(alias="col1")]] + col2: NotRequired[Annotated[PublicAView, Field(alias="col2")]] + + class PublicTableWithOtherTablesRowTypeUpdate(TypedDict): + col1: NotRequired[Annotated[PublicUserDetails, Field(alias="col1")]] + col2: NotRequired[Annotated[PublicAView, Field(alias="col2")]] + + class PublicTableWithPrimaryKeyOtherThanId(BaseModel): + name: Optional[str] = Field(alias="name") + other_id: int = Field(alias="other_id") + + class PublicTableWithPrimaryKeyOtherThanIdInsert(TypedDict): + name: NotRequired[Annotated[str, Field(alias="name")]] + other_id: NotRequired[Annotated[int, Field(alias="other_id")]] + + class PublicTableWithPrimaryKeyOtherThanIdUpdate(TypedDict): + name: NotRequired[Annotated[str, Field(alias="name")]] + other_id: NotRequired[Annotated[int, Field(alias="other_id")]] + + class PublicEvents(BaseModel): + created_at: datetime.datetime = Field(alias="created_at") + data: Optional[Json[Any]] = Field(alias="data") + event_type: Optional[str] = Field(alias="event_type") + id: int = Field(alias="id") + + class PublicEventsInsert(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + data: NotRequired[Annotated[Json[Any], Field(alias="data")]] + event_type: NotRequired[Annotated[str, Field(alias="event_type")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + + class PublicEventsUpdate(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + data: NotRequired[Annotated[Json[Any], Field(alias="data")]] + event_type: NotRequired[Annotated[str, Field(alias="event_type")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + + class PublicEvents2024(BaseModel): + created_at: datetime.datetime = Field(alias="created_at") + data: Optional[Json[Any]] = Field(alias="data") + event_type: Optional[str] = Field(alias="event_type") + id: int = Field(alias="id") + + class PublicEvents2024Insert(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + data: NotRequired[Annotated[Json[Any], Field(alias="data")]] + event_type: NotRequired[Annotated[str, Field(alias="event_type")]] + id: Annotated[int, Field(alias="id")] + + class PublicEvents2024Update(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + data: NotRequired[Annotated[Json[Any], Field(alias="data")]] + event_type: NotRequired[Annotated[str, Field(alias="event_type")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + + class PublicEvents2025(BaseModel): + created_at: datetime.datetime = Field(alias="created_at") + data: Optional[Json[Any]] = Field(alias="data") + event_type: Optional[str] = Field(alias="event_type") + id: int = Field(alias="id") + + class PublicEvents2025Insert(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + data: NotRequired[Annotated[Json[Any], Field(alias="data")]] + event_type: NotRequired[Annotated[str, Field(alias="event_type")]] + id: Annotated[int, Field(alias="id")] + + class PublicEvents2025Update(TypedDict): + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + data: NotRequired[Annotated[Json[Any], Field(alias="data")]] + event_type: NotRequired[Annotated[str, Field(alias="event_type")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + + class PublicCategory(BaseModel): + id: int = Field(alias="id") + name: str = Field(alias="name") + + class PublicCategoryInsert(TypedDict): + id: NotRequired[Annotated[int, Field(alias="id")]] + name: Annotated[str, Field(alias="name")] + + class PublicCategoryUpdate(TypedDict): + id: NotRequired[Annotated[int, Field(alias="id")]] + name: NotRequired[Annotated[str, Field(alias="name")]] + + class PublicMemes(BaseModel): + category: Optional[int] = Field(alias="category") + created_at: datetime.datetime = Field(alias="created_at") + id: int = Field(alias="id") + metadata: Optional[Json[Any]] = Field(alias="metadata") + name: str = Field(alias="name") + status: Optional[PublicMemeStatus] = Field(alias="status") + + class PublicMemesInsert(TypedDict): + category: NotRequired[Annotated[int, Field(alias="category")]] + created_at: Annotated[datetime.datetime, Field(alias="created_at")] + id: NotRequired[Annotated[int, Field(alias="id")]] + metadata: NotRequired[Annotated[Json[Any], Field(alias="metadata")]] + name: Annotated[str, Field(alias="name")] + status: NotRequired[Annotated[PublicMemeStatus, Field(alias="status")]] + + class PublicMemesUpdate(TypedDict): + category: NotRequired[Annotated[int, Field(alias="category")]] + created_at: NotRequired[Annotated[datetime.datetime, Field(alias="created_at")]] + id: NotRequired[Annotated[int, Field(alias="id")]] + metadata: NotRequired[Annotated[Json[Any], Field(alias="metadata")]] + name: NotRequired[Annotated[str, Field(alias="name")]] + status: NotRequired[Annotated[PublicMemeStatus, Field(alias="status")]] + + class PublicAView(BaseModel): + id: Optional[int] = Field(alias="id") + + class PublicTodosView(BaseModel): + details: Optional[str] = Field(alias="details") + id: Optional[int] = Field(alias="id") + user_id: Optional[int] = Field(alias="user-id") + + class PublicUsersView(BaseModel): + decimal: Optional[float] = Field(alias="decimal") + id: Optional[int] = Field(alias="id") + name: Optional[str] = Field(alias="name") + status: Optional[PublicUserStatus] = Field(alias="status") + user_uuid: Optional[uuid.UUID] = Field(alias="user_uuid") + + class PublicUserTodosSummaryView(BaseModel): + todo_count: Optional[int] = Field(alias="todo_count") + todo_details: Optional[List[str]] = Field(alias="todo_details") + user_id: Optional[int] = Field(alias="user_id") + user_name: Optional[str] = Field(alias="user_name") + user_status: Optional[PublicUserStatus] = Field(alias="user_status") + + class PublicUsersViewWithMultipleRefsToUsers(BaseModel): + initial_id: Optional[int] = Field(alias="initial_id") + initial_name: Optional[str] = Field(alias="initial_name") + second_id: Optional[int] = Field(alias="second_id") + second_name: Optional[str] = Field(alias="second_name") + + class PublicTodosMatview(BaseModel): + details: Optional[str] = Field(alias="details") + id: Optional[int] = Field(alias="id") + user_id: Optional[int] = Field(alias="user-id") + + class PublicCompositeTypeWithArrayAttribute(BaseModel): + my_text_array: List[str] = Field(alias="my_text_array") + + class PublicCompositeTypeWithRecordAttribute(BaseModel): + todo: PublicTodos = Field(alias="todo")" + `) +}) + +test('typegen: python w/ excluded/included schemas', async () => { + // Create a test schema with some tables + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + CREATE SCHEMA IF NOT EXISTS test_schema; + CREATE TABLE IF NOT EXISTS test_schema.test_table ( + id serial PRIMARY KEY, + name text + ); + CREATE TABLE IF NOT EXISTS test_schema.another_table ( + id serial PRIMARY KEY, + value text + ); + `, + }, + }) + + try { + // Test excluded_schemas - should exclude test_schema + const { body: excludedBody } = await app.inject({ + method: 'GET', + path: '/generators/python', + query: { access_control: 'public', excluded_schemas: 'test_schema' }, + }) + expect(excludedBody).not.toContain('TestSchemaTestTable') + expect(excludedBody).not.toContain('TestSchemaAnotherTable') + expect(excludedBody).toContain('PublicUsers') + expect(excludedBody).toContain('PublicTodos') + + // Test included_schemas - should only include test_schema + const { body: includedBody } = await app.inject({ + method: 'GET', + path: '/generators/python', + query: { access_control: 'public', included_schemas: 'test_schema' }, + }) + expect(includedBody).toContain('TestSchemaTestTable') + expect(includedBody).toContain('TestSchemaAnotherTable') + expect(includedBody).not.toContain('PublicUsers') + expect(includedBody).not.toContain('PublicTodos') + + // Test multiple excluded schemas + const { body: multipleExcludedBody } = await app.inject({ + method: 'GET', + path: '/generators/python', + query: { access_control: 'public', excluded_schemas: 'test_schema,public' }, + }) + expect(multipleExcludedBody).not.toContain('TestSchemaTestTable') + expect(multipleExcludedBody).not.toContain('PublicUsers') + + // // Test multiple included schemas + const { body: multipleIncludedBody } = await app.inject({ + method: 'GET', + path: '/generators/python', + query: { access_control: 'public', included_schemas: 'public,test_schema' }, + }) + expect(multipleIncludedBody).toContain('TestSchemaTestTable') + expect(multipleIncludedBody).toContain('PublicUsers') + } finally { + // Clean up test schema + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + DROP SCHEMA IF EXISTS test_schema CASCADE; + `, + }, + }) + } +}) diff --git a/test/server/utils.ts b/test/server/utils.ts index 0222fcf3..63e1e53d 100644 --- a/test/server/utils.ts +++ b/test/server/utils.ts @@ -1,3 +1,29 @@ import { build as buildApp } from '../../src/server/app' export const app = buildApp() + +/** + * Normalizes UUIDs in test data to make snapshots resilient to UUID changes. + * Replaces all UUID strings with a consistent placeholder. + */ +export function normalizeUuids(data: unknown): unknown { + const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i + + if (typeof data === 'string' && uuidRegex.test(data)) { + return '00000000-0000-0000-0000-000000000000' + } + + if (Array.isArray(data)) { + return data.map(normalizeUuids) + } + + if (data !== null && typeof data === 'object') { + const normalized: Record = {} + for (const [key, value] of Object.entries(data)) { + normalized[key] = normalizeUuids(value) + } + return normalized + } + + return data +}